Shadcn Form Controls¶
Use shadcn/ui primitives for all user-facing React form controls in apps and shared UI surfaces.
Core Contract¶
- Do not render user-facing native
<input>,<select>, or<textarea>directly in feature code. - Use local shadcn primitives instead:
- text-like fields:
Input - multiline fields:
Textarea - option menus:
Select - booleans:
CheckboxorSwitch - single-choice sets:
RadioGroup/RadioGroupItem - small mutually exclusive toggles:
ToggleGroupwhen the app has that primitive
- text-like fields:
- Do not build custom
role="radiogroup",role="combobox", or select-like widgets from buttons/divs when a shadcn/Radix primitive exists. - Preserve existing form behavior when migrating, especially React Hook Form bindings, URL-driven GET filters, server-action payloads,
data-testidhooks, and i18n labels. - For search-bar placeholder and
aria-labelsemantics, follow058-search-input-ux-policy.mdc.
Labels And Accessibility¶
- Every visible control must have a visible label and a programmatic association.
- Exception: search/filter bars may use
placeholder+aria-labelwithout a visible label, per058-search-input-ux-policy.mdc. - For shadcn
Select, preferLabel/labelplusSelectTriggerwithidandaria-labelledbywhen needed. - Validation and helper text must remain wired with
aria-invalidandaria-describedby. - Radio groups must expose a group label via visible text,
aria-label, oraria-labelledby.
Allowed Exceptions¶
- Native fields inside
src/components/ui/*shadcn primitive implementations are expected. - Hidden or programmatic inputs are allowed when they are plumbing only, such as file-picker triggers or hidden fields preserving form/query behavior.
Input type="color"is allowed for color wells; do not use a raw native<input type="color">in feature code.- Native
datalistis allowed only as an intentional lightweight suggestion layer on top of shadcnInput; document the reason near thedatalist. - Shared package plain-HTML fallback adapters may use native elements for host-agnostic rendering, but production app adapters should map controls to shadcn primitives.
Migration Checklist¶
- Add missing shadcn primitives to the app before importing them.
- For React Hook Form, use
Controllerwhen replacing native selects or composite primitives. - For URL-driven filters, keep query serialization stable and preserve pagination/search params.
- Update focused component tests for value changes, submit/query behavior, keyboard access, and error states.
- Run full repo validation after changes:
npm run check.