React Hook Form

Knowledge base from the React Hook Form documentation — core useForm API, register, Controller/useController, useFieldArray, useWatch/useFormState, TypeScript types, and the @hookform/lenses and @hookform/error-message companion packages. Use when building or debugging a React form, choosing between register and Controller, wiring up schema validation (Zod/Yup/Joi), managing dynamic field arrays, or optimizing re-renders in a large form.

39 capítulos

React Hook Form

Package: react-hook-form | Chapters: 39 | Generated: 2026-08-25

How to Use This Skill

  • Without arguments — load the Core Patterns below for the composition/re-render conventions that recur across the whole API.
  • With a topic — ask about register, Controller, useFieldArray, schema validation, or another indexed topic; the relevant chapter gets read before answering.
  • With a chapter — ask for ch15 (or any chNN); that specific chapter loads directly.
  • Browse — ask "what chapters do you have?" to see the full index below.

When a question isn't covered by Core Patterns, the relevant chapter file is read before answering, rather than guessing from general React knowledge — RHF's API has enough version-specific and option-specific behavior (v7 vs. v8, mode vs. reValidateMode, exact defaults that differ per hook) that guessing is unreliable.


Core Patterns & Conventions

Uncontrolled by default. register(name) returns { name, ref, onChange, onBlur } to spread onto a native input; RHF reads the value via ref, not React state, which is why it re-renders far less than a fully-controlled form. Use defaultValue/defaultValues, never value, on a registered input.

Controlled inputs go through Controller/useController. Any component without a native ref/value/onChange contract (MUI, react-select, date pickers) needs <Controller render={({field}) => ...} /> or useController. Never register and Controller the same field.

Three tiers of read API, by re-render scope. Root-level (watch(), formState from useForm()) re-renders the whole form-holding component. Isolated (useWatch, useFormState) re-renders only the calling component. No-render (getValues, getErrors, subscribe) never triggers React at all. Default to the isolated tier in any form bigger than a handful of fields.

control is the connective tissue. Every controlled-input API (Controller, useController, useWatch, useFieldArray, useFormState) needs it, unless a FormProvider supplies it implicitly. Treat it as opaque — never read its internals directly.

Validation is rules, resolver, or both — but resolver wins if both are configured. Inline register rules (required, pattern, validate, …) are the default; a resolver (Zod/Yup/Joi/etc. via @hookform/resolvers) replaces them wholesale for schema-driven validation. useForm's own validate option is a third, form-level option, mutually exclusive with resolver.

mode/reValidateMode control validation timing, not what gets validated. Default onSubmit validates once on submit then re-validates changed fields on change; onChange is the most expensive; onBlur/onTouched sit in between.

isDirty/isValid are comparisons, not raw flags. isDirty is always live-computed against defaultValues; isValid needs at least one real validation pass (a resolver, a non-default mode, or trigger()) before it means anything — setError can force it false but that's an override, not a validation result.

Field arrays key by field.id, never index. useFieldArray's fields entries carry a stable auto-generated id; using array index as the React key breaks row identity across reorder/insert/remove.

root.* is the namespace for errors that aren't about one field. setError("root.serverError", {...}) for a failed async submission; these don't persist submission-to-submission the way field errors do.

Context (FormProvider) removes prop drilling but widens re-renders. useFormContext() reads the shared useForm() return in any descendant; pair it with useFormState/useWatch (not a destructured-and-later-read formState) to keep re-renders scoped inside a provider subtree.


Chapter Index

Guides

#TitleKey Concepts
ch01Get Startedregister, handleSubmit, watch, formState.errors, resolver
ch02Advanced Usageaccessibility, FormProvider recipes, virtualized lists, Server Actions
ch03TypeScript SupportResolver, UseFormReturn, FieldPath, RegisterOptions
ch04Migrate V7 to V8 (Beta)field.ref semantics, useFieldArray id→key, subscribe
ch05FAQsuncontrolled performance, defaultValue vs value, watch vs getValues

useForm (root hook)

#TitleKey Concepts
ch06useFormmode, resolver, defaultValues, shouldUnregister, progressive
ch07clearErrorsnamespace clearing, doesn't affect isValid
ch08controlopaque connector object
ch09Form (beta)action, Server Actions, root.server, progressive
ch10formStateisDirty, dirtyFields, isValid, isReady
ch11getErrorsnon-reactive error read
ch12getFieldStateper-field combined state
ch13getValuesnon-reactive value read
ch14handleSubmitSubmitHandler, TTransformedValues, root.serverError
ch15registervalidation rules, valueAsNumber, deps
ch16resetkeepDirty, keepValues, keepDefaultValues
ch17resetDefaultValuesbaseline-only reset
ch18resetFieldsingle-field reset
ch19setErrorroot.*, types, shouldFocus
ch20setFocusprogrammatic focus
ch21setValueshouldValidate, shouldDirty, dependent fields
ch22setValuesbatched multi-field write
ch23subscribeno-render subscription
ch24triggermanual on-demand validation
ch25unregisterremoves ref/rules, not schema
ch26watchroot-level value subscription

Controller / useController

#TitleKey Concepts
ch27useControllerfield, fieldState, formState
ch28Controllerrender prop, third-party inputs

useFieldArray

#TitleKey Concepts
ch29useFieldArrayappend/prepend/insert/swap/move/update/replace/remove
ch30FieldArrayrender-prop wrapper

Context & form state

#TitleKey Concepts
ch31useFormContextFormProvider consumer
ch32useFormStateisolated state subscription
ch33ErrorMessage@hookform/error-message
ch34FormState (component)renderless formState subscription
ch38FormProviderContext host
ch39createFormControlstandalone form state outside React

Watching values

#TitleKey Concepts
ch35useWatchisolated value subscription, compute
ch36Watch (component)renderless value subscription

Advanced

#TitleKey Concepts
ch37useLens@hookform/lenses, focus, reflect, map

Topic Index

  • Accessibility → ch02
  • Async validation → ch15
  • compute (selector) → ch35, ch36
  • Controller / controlled inputs → ch27, ch28, ch02
  • createFormControl → ch39
  • defaultValues → ch06, ch16
  • Dependent/derived fields → ch21, ch26, ch35
  • Dirty state → ch10, ch12, ch17, ch32
  • ErrorMessage → ch33
  • Field arrays → ch29, ch30
  • FormProvider / Context → ch38, ch31
  • isValid → ch06, ch10, ch32
  • Lenses (@hookform/lenses) → ch37
  • Migration (v7→v8) → ch04
  • mode / reValidateMode → ch06
  • Progressive enhancement → ch06, ch09, ch15
  • register → ch15
  • reset (all variants) → ch16, ch17, ch18
  • Resolver / schema validation (Zod/Yup/Joi) → ch03, ch06, ch01
  • Server Actions / server errors → ch02, ch09, ch14, ch19
  • setError / clearErrors → ch19, ch07
  • setValue / setValues → ch21, ch22
  • subscribe (no-render) → ch23, ch39
  • trigger (manual validation) → ch24
  • TypeScript types → ch03
  • unregister → ch25
  • Virtualized lists → ch02
  • watch / useWatch / Watch → ch26, ch35, ch36

Supporting Files

Scope & Limits

This skill covers the React Hook Form v7/v8-beta core documentation (39 chapters: guides, the full useForm API surface, Controller/useController, useFieldArray, form-state/watch APIs, and the @hookform/lenses and @hookform/error-message companion packages), synthesized from the official docs as of the fetch date recorded in the source file. It does not cover the @hookform/resolvers adapters' internals (only their usage from RHF's side), React Native specifics beyond what's shown inline in the guide pages, or third-party UI library integration details beyond the Controller pattern itself.