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.
Package: react-hook-form | Chapters: 39 | Generated: 2026-08-25
register, Controller, useFieldArray, schema validation, or another indexed topic; the relevant chapter gets read before answering.ch15 (or any chNN); that specific chapter loads directly.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.
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.
| # | Title | Key Concepts |
|---|---|---|
| ch01 | Get Started | register, handleSubmit, watch, formState.errors, resolver |
| ch02 | Advanced Usage | accessibility, FormProvider recipes, virtualized lists, Server Actions |
| ch03 | TypeScript Support | Resolver, UseFormReturn, FieldPath, RegisterOptions |
| ch04 | Migrate V7 to V8 (Beta) | field.ref semantics, useFieldArray id→key, subscribe |
| ch05 | FAQs | uncontrolled performance, defaultValue vs value, watch vs getValues |
| # | Title | Key Concepts |
|---|---|---|
| ch06 | useForm | mode, resolver, defaultValues, shouldUnregister, progressive |
| ch07 | clearErrors | namespace clearing, doesn't affect isValid |
| ch08 | control | opaque connector object |
| ch09 | Form (beta) | action, Server Actions, root.server, progressive |
| ch10 | formState | isDirty, dirtyFields, isValid, isReady |
| ch11 | getErrors | non-reactive error read |
| ch12 | getFieldState | per-field combined state |
| ch13 | getValues | non-reactive value read |
| ch14 | handleSubmit | SubmitHandler, TTransformedValues, root.serverError |
| ch15 | register | validation rules, valueAsNumber, deps |
| ch16 | reset | keepDirty, keepValues, keepDefaultValues |
| ch17 | resetDefaultValues | baseline-only reset |
| ch18 | resetField | single-field reset |
| ch19 | setError | root.*, types, shouldFocus |
| ch20 | setFocus | programmatic focus |
| ch21 | setValue | shouldValidate, shouldDirty, dependent fields |
| ch22 | setValues | batched multi-field write |
| ch23 | subscribe | no-render subscription |
| ch24 | trigger | manual on-demand validation |
| ch25 | unregister | removes ref/rules, not schema |
| ch26 | watch | root-level value subscription |
| # | Title | Key Concepts |
|---|---|---|
| ch27 | useController | field, fieldState, formState |
| ch28 | Controller | render prop, third-party inputs |
| # | Title | Key Concepts |
|---|---|---|
| ch29 | useFieldArray | append/prepend/insert/swap/move/update/replace/remove |
| ch30 | FieldArray | render-prop wrapper |
| # | Title | Key Concepts |
|---|---|---|
| ch31 | useFormContext | FormProvider consumer |
| ch32 | useFormState | isolated state subscription |
| ch33 | ErrorMessage | @hookform/error-message |
| ch34 | FormState (component) | renderless formState subscription |
| ch38 | FormProvider | Context host |
| ch39 | createFormControl | standalone form state outside React |
| # | Title | Key Concepts |
|---|---|---|
| ch35 | useWatch | isolated value subscription, compute |
| ch36 | Watch (component) | renderless value subscription |
| # | Title | Key Concepts |
|---|---|---|
| ch37 | useLens | @hookform/lenses, focus, reflect, map |
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.