Review admin UI code for consistency with established patterns. Use after writing any UI code in packages/admin to catch anti-patterns — wrong form components, hardcoded strings, missing i18n, incorrect heading levels, manual error rendering, missing data-testid, raw Controller usage.
Use this skill when:
This skill checks ALL admin UI patterns. For implementation guidance, see:
admin-form-ui — form field patternsadmin-page-ui — page and section patternsadmin-tab-ui — tab patterns in TabbedForm wizardspackages/admin/src/Form.FieldControllerForm.Item wrapper (not manual <div className="flex flex-col gap-y-2">)Form.Label (not <Label> from @medusajs/ui)Form.Control around input elementsForm.ErrorMessage (not manual fieldState.error && <span>)* in label text — just omit optional prop<Form.Label optional> is presentForm.Hint component (not custom divs)t("...") from useTranslation()t("scope.fields.field_name.label")t("scope.fields.field_name.placeholder")t("scope.section.header")t("actions.save"), t("actions.cancel"), t("actions.create"), t("actions.delete")t("scope.action.successToast")labelKey: "scope.create.tabs.tab_name" (not raw text)<Heading> (default level, no level prop)<Heading level="h2"> (inside Container/card headers)<Heading level="h2"> (NEVER h1 in tabs)<Heading> inside RouteDrawer.Header<Text size="small"> or <Text size="small" className="text-ui-fg-subtle"><h1>, <h2>, <p> tags — use Heading/Text componentsdefineTabMeta<SchemaType>({...}) (not raw object { id, label, ... })labelKey (i18n key, not label with raw text)validationFields array listing fields for this tabp-16, inner max-w-[720px] + gap-y-8gap-y-8 between sections, gap-y-6 between fieldsgrid-cols-1 gap-4 md:grid-cols-{2,3} (not custom breakpoints)Container className="divide-y p-0" with header px-6 py-4py-16 + centered max-w-[720px]flex items-center justify-end gap-x-2<li> with bg-ui-bg-component shadow-elevation-card-rest rounded-xl p-1.5grid grid-cols-[1fr_28px] (content + delete button)grid grid-cols-[min-content,1fr] (label + input pairs horizontally)<Label size="xsmall" weight="plus" className="text-ui-fg-subtle"> (compact inline labels — NOT Form.Label)className="bg-ui-bg-field-component" for contrast background<IconButton size="small" variant="transparent"> with <XMarkMini />useFieldArray with append/removeForm.Label + Form.Hint + Add <Button size="small" variant="secondary">useWatch + isOptionDisabled pattern when rows select from shared poolActionMenu component (not custom dropdowns)usePrompt() (not window.confirm)NoRecords / NoResults (not custom empty divs)StatusBadge (not custom colored spans)KeyboundForm (not raw <form>)RouteDrawer.Close / RouteFocusModal.Close (not manual navigate)KeyboundForm (not raw <form>)isPending guardisLoading={isPending}toast.success() + handleSuccess() from useRouteModal()isError → throw error for error boundarydata-testid on root elements, form fields, buttonsdata-testid="product-create-title-input"## UI Review: {file_path}
### P1 (Critical)
1. **Raw Controller** at line {N}: Uses `Controller` instead of `Form.Field`
Fix: Replace with `Form.Field` + `Form.Item` + `Form.Label` + `Form.Control` + `Form.ErrorMessage`
2. **Hardcoded string** at line {N}: `"SEO Title *"` should be `t("products.fields.seo_title.label")`
### P2 (Inconsistency)
1. **Wrong heading level** at line {N}: `<Heading level="h1">` in tab — should be `level="h2"`
### P3 (Style)
1. **Missing data-testid** at line {N}: Tab root div missing `data-testid`
## UI Review: {file_path}
No findings. Code follows all admin UI patterns.
Verified:
- Form.Field pattern ✓
- i18n strings ✓
- Correct heading levels ✓
- defineTabMeta ✓
- data-testid ✓
| Anti-Pattern | Correct Pattern |
|---|---|
<Controller> | <Form.Field> |
<Label> from @medusajs/ui | <Form.Label> |
fieldState.error && <span> | <Form.ErrorMessage /> |
"Title *" | <Form.Label>{t("...")}</Form.Label> (no optional) |
_tabMeta = { id: "x" } | defineTabMeta<T>({ id: "x", ... }) |
<Heading level="h1"> in tab | <Heading level="h2"> |
window.confirm(...) | usePrompt() |
<form onSubmit> | <KeyboundForm onSubmit> |
<div className="flex flex-col gap-y-2"> (field wrapper) | <Form.Item> |
Hardcoded "Save" | t("actions.save") |