edbnedbn/uibeta
ComponentsTablesChartsPDFs
GitHub stars
Explorer
    • Welcome
    • Principles
    • Accordion
    • Alert
    • Alert Dialog
    • Aspect Ratio
    • Attachment
    • Avatar
    • Badge
    • Breadcrumb
    • Bubble
    • Button
    • Button Group
    • Calendar
    • Card
    • Carousel
    • Chart
    • Checkbox
    • Collapsible
    • Combobox
    • Command
    • Context Menu
    • Data Table
    • Date Picker
    • Basic
    • Range
    • Date of Birth
    • Input
    • Time
    • Natural Language
    • RTL
    • Dialog
    • Direction
    • Drawer
    • Dropdown Menu
    • Empty
    • Field
    • Form
    • Bug Report
    • Select
    • Checkbox Group
    • Radio Group
    • Switch
    • Complex Form
    • Array Fields
    • Hover Card
    • Input
    • Input Group
    • Input Otp
    • Item
    • Kbd
    • Label
    • Marker
    • Menubar
    • Message
    • Message Scroller
    • Native Select
    • Navigation Menu
    • Pagination
    • Popover
    • Progress
    • Questionnaire
    • Radio Group
    • Resizable
    • Scroll Area
    • Select
    • Separator
    • Sheet
    • Sidebar
    • Skeleton
    • Slider
    • Sonner
    • Spinner
    • Switch
    • Table
    • Tabs
    • Textarea
    • Toast
    • Toggle
    • Toggle Group
    • Tooltip
    • Typeset
    • Documentation
    • Reading
    • Chat / Streaming
    • Element Coverage

Composition recipe

Form

Build accessible, validated forms with React Hook Form, Zod, and the Field family.

React Hook Formshadcn reference

Installation

Components

npx shadcn@4.19.0 add https://ui.edbn.me/r/field.json https://ui.edbn.me/r/input.json https://ui.edbn.me/r/input-group.json https://ui.edbn.me/r/textarea.json https://ui.edbn.me/r/button.json https://ui.edbn.me/r/select.json https://ui.edbn.me/r/checkbox.json https://ui.edbn.me/r/radio-group.json https://ui.edbn.me/r/switch.json

Form libraries

npm install react-hook-form @hookform/resolvers zod

Source

Dependencies

Packages3

  • react-hook-form
  • @hookform/resolvers
  • zod

Registry items9

  • field
  • input
  • input-group
  • textarea
  • button
  • select
  • checkbox
  • radio-group
  • switch

Documentation

Approach

React Hook Form owns field state and submission. Zod owns the schema, while the Field family owns labels, descriptions, invalid styling, and announced error messages. The recipe remains application code so its data model does not leak into a generic wrapper.

Put data-invalid on Field, pass aria-invalid to the interactive control, and render the controller error through FieldError.

Schema and form setup

const schema = z.object({
  title: z.string().min(5, "Use at least 5 characters."),
})

const form = useForm<z.infer<typeof schema>>({
  resolver: zodResolver(schema),
  defaultValues: { title: "" },
})

Supply every controlled field with a default value. This keeps the first client render aligned with the server and makes reset behavior predictable.

Controller pattern

<Controller
  name="title"
  control={form.control}
  render={({ field, fieldState }) => (
    <Field data-invalid={fieldState.invalid}>
      <FieldLabel htmlFor={field.name}>Title</FieldLabel>
      <Input {...field} id={field.name} aria-invalid={fieldState.invalid} />
      <FieldError errors={[fieldState.error]} />
    </Field>
  )}
/>

Use the same bridge for Base UI Select, Checkbox, Radio Group, and Switch. Translate their value callbacks to field.onChange instead of registering hidden inputs.

Examples

  • Bug Report demonstrates validation, submit status, and reset.
  • Select, Checkbox Group, Radio Group, and Switch cover controlled primitives.
  • Complex Form combines multiple field types without a form wrapper.
  • Array Fields demonstrates dynamic fields with stable keys from useFieldArray.

Accessibility and recovery

  • Keep a visible label associated with every control.
  • Let React Hook Form focus the first invalid control after submit.
  • Use live status text for successful asynchronous submissions.
  • Reset both form state and any success message exposed by the feature.

The component building blocks are documented under Field, Input, and Select.