1. Docs
  2. Forms
  3. React Hook Form

React Hook Form

Build forms with React Hook Form and Zod validation.

This guide shows how to build forms using React Hook Form with Kanpeki's Field component and Zod validation.

Demo

Approach

React Hook Form provides performant, uncontrolled form state management. We combine it with:

  • Field components for layout and accessibility
  • TextField for automatic ID wiring between label, input, description, and error
  • Controller for wiring controlled Kanpeki inputs
  • zodResolver from @hookform/resolvers for schema validation

Anatomy

TextField is Kanpeki's React Aria behavior adapter. It automatically wires Field.Label, Field.Description, and Field.Error via aria-labelledby and aria-describedby using unique generated IDs. See the forms architecture for more.

isInvalid drives error styling. Destructuring ref and disabled from the field lets you pass ref to <Input /> and forward isDisabled to <TextField />, while spreading the rest wires name, value, onChange, and onBlur automatically.

Setup

Install dependencies

Create a schema

Setup the form

Use useForm with zodResolver:

Validation

Validation Modes

React Hook Form validates on submit by default. Configure with the mode option:

ModeDescription
onSubmitValidate on form submission (default)
onBlurValidate when field loses focus
onChangeValidate on every change
onTouchedValidate on first blur, then on change
allValidate on both blur and change

Displaying Errors

Use <Field.Error /> with the errors prop. Pass [fieldState.error] directly — Field.Error ignores undefined entries so it renders nothing when the field is valid:

Field Types

Input

Textarea

Select

Pass <Select.Root /> as the render prop on <Field.Root />. Use value and onChange for RHF wiring:

Checkbox

For checkbox groups, pass <Checkbox.Provider /> as the render prop on each <Field.Root /> and toggle array items on change:

Switch

Switch.Root is a standalone RAC component — pass name, isSelected, and onChange directly. No TextField wrapper is needed since a switch has no text input:

Submit Button State

Disable the submit button while submitting using isSubmitting from formState:

Resetting the Form

Use form.reset() to restore default values:

Array Fields

React Hook Form supports dynamic array fields with useFieldArray:

Array Helpers

MethodDescription
append(item)Add item to end of array
prepend(item)Add item to start
remove(i)Remove item at index
insert(i, item)Insert item at index
swap(a, b)Swap items at indices
move(from, to)Move item to new index
update(i, item)Replace item at index
replace(items)Replace entire array

Linked Fields

When one field's validation depends on another (e.g. confirm password), use Zod's .refine() at the schema level to cross-validate:

The path option routes the error to the correct field. React Hook Form surfaces it via fieldState.error on the confirmPassword controller.