| | |

React Hook Form 26 📋 Cheatsheet

⏱️ Estimated read: 4 min | |

useForm Options

OptionTypeDefaultNotes
resolverResolver—Schema validation (Zod, Yup); mutually exclusive with validate
mode'onSubmit' | 'onChange' | 'onBlur' | 'all''onSubmit'When to validate
reValidateMode'onChange' | 'onBlur''onChange'Re-validation after first error
defaultValuesobject | () => object | Promise<object>{}Required for dirty tracking and controlled inputs
valuesobject—Controlled form values; updates form when data changes
shouldUnregisterbooleanfalseRemove values on unmount; incompatible with useFieldArray
shouldFocusErrorbooleantrueFocus first invalid field on submit
criteriaMode'firstError' | 'all''firstError'Return first or all errors per field
delayErrornumber—Delay error display in ms
shouldUseNativeValidationbooleanfalseEnable browser native validation (v7.9+)
progressivebooleanfalseForward rules as native HTML attributes (v7.44+)
disabledbooleanfalseDisable entire form (v7.48+)

register Validation Rules

RuleTypeExampleNotes
requiredboolean | stringrequired: "Email is required"String becomes message
min / maxnumber | { value, message }min: { value: 18, message: "18+" }Number comparison
minLength / maxLength{ value, message }minLength: { value: 8, message: "8+" }String length
patternRegExp | { value, message }pattern: { value: /^\S+@\S+$/, message: "Invalid" }Avoid /g flag; it tracks lastIndex
validateFunction | Record<string, Function>validate: (v) => v !== "admin" || "Reserved"Receives value; v7.42+ also full form values
valueAsNumberbooleanvalueAsNumber: trueReturns NaN on failure
valueAsDatebooleanvalueAsDate: trueReturns Invalid Date on failure
setValueAs(value: any) => TsetValueAs: (v) => v.trim()Ignored if valueAsNumber or valueAsDate is true
disabledbooleandisabled: trueReturns undefined; skips validation (v7.13+)
depsstring | string[]deps: ["password"]Re-validate listed fields when this field validates (v7.14+)
shouldUnregisterbooleanshouldUnregister: trueField-level unmount behavior; avoid with useFieldArray
onChange(e) => voidonChange: (e) => log(e.target.value)Called on change event (v7.16+)
onBlur(e) => voidonBlur: (e) => log(e.target.value)Called on blur event

formState Properties

PropertyTypeMeaningNotes
errorsFieldErrorsValidation messages per fieldUse ErrorMessage component or read manually
isDirtybooleanAny field differs from defaultValuesRequires defaultValues
dirtyFieldsobjectWhich fields changedField-level; does not equal isDirty
touchedFieldsobjectWhich fields blurred—
defaultValuesobjectCurrent defaults (v7.37+)Updated via reset
isSubmittedbooleanAt least one submit attemptedStays true until reset()
isSubmitSuccessfulbooleanLast submission succeededNo runtime error
isSubmittingbooleanonSubmit promise is pendingMust await or return the promise
isLoadingbooleanAsync defaultValues loading (v7.41+)Only for async defaults
submitCountnumberTotal submit attempts—
isValidbooleanNo errorssetError forces false immediately
isValidatingbooleanValidation in progress—
validatingFieldsobjectFields in async validation (v7.51+)—
disabledbooleanForm disabled via useForm (v7.48+)—
isReadybooleanSubscription setup ready (v7.56+)Use before setValue in children

Core Methods

MethodSignatureNotes
registerregister("name", rules)Spread onto input: {...register("email")}
handleSubmithandleSubmit(onValid, onInvalid?)Wraps form onSubmit
watchwatch("name") / watch(["a","b"]) / watch()Re-renders calling component; isolate to specific field
useWatchuseWatch({ control, name })Re-renders only the component that calls it
getValuesgetValues("name") / getValues()No re-render; read in handlers
setValuesetValue("name", v, { shouldValidate, shouldDirty, shouldTouch })Programmatic set
resetreset(values?, { keepErrors, keepDirty, keepIsSubmitted, keepTouched, keepIsValid, keepSubmitCount })Back to defaultValues
triggertrigger("name"?) / trigger(["a","b"])Manual validation; returns Promise
setErrorsetError("name", { type, message })Server-side errors; forces isValid false
clearErrorsclearErrors("name"?)Remove errors
setFocussetFocus("name")Focus a field
unregisterunregister("name")Remove field registration

useFieldArray (v7)

ItemSyntaxNotes
Hookconst { fields, append, prepend, insert, remove, swap, move, update, replace } = useFieldArray({ control, name })control required
Render keykey={field.id}Never use index
Appendappend({ value: "" })Cannot be empty object
Prependprepend({ value: "" })Add at start
Insertinsert(index, { value: "" })Insert at position
Removeremove(index)—
Swapswap(from, to)Swap two rows
Movemove(from, to)Move row
Updateupdate(index, { value: "" })Unmounts and remounts; prefer setValue for simple updates
Replacereplace(newArray)Overwrite entire array
Row typeMust be object (v7){ value: string }[], not string[]
shouldUnregisterNot supportedDo not combine with useFieldArray

watch vs useWatch vs subscribe vs getValues

APIRe-rendersUse For
watchThe component calling useFormSimple form-level display; expensive on large forms
useWatchOnly the component calling itDeep rendering: live totals, conditional fields
subscribeNothingSide effects: autosave, analytics, external sync
getValuesNothingOn-demand reads in event handlers
useFormStateWhen subscribed state changeserrors, isDirty, isSubmitting (not values)

Controller / useController

ItemNotes
Hookconst { field, fieldState, formState } = useController({ name, control, rules })
field.onChangeSend value to RHF
field.onBlurMark touched
field.valueCurrent value
field.nameField name
field.refRequired for focus management
field.disabledDisabled state
fieldState.invalidError boolean
fieldState.errorError object
fieldState.isTouchedTouched state
fieldState.isDirtyDirty state
fieldState.isValidatingAsync validation in progress

Error Display Patterns

PatternAttributeUse
Visible messageerrors.field?.messageRender in <p> below input
Screen reader statearia-invalid={!!errors.field}Announces invalid state
Error linkagearia-describedby={errors.field ? "field-error" : undefined}Links message to input
Server errorsetError("field", { type: "server", message: "..." })After API response
Root errorsetError("root.serverError", { message: "..." })Form-level error
Immediate announcerole="alert" on error messageSingle errors only; noisy on submit with many

Controlled Input Patterns

Input TypePattern
Native text/number{...register("name")}
Checkbox{...register("name")}
Radio groupSame name, one register per input
Select{...register("name")}
Third-party UI<Controller> with field spread
Fileregister + watch for preview
Nested objectregister("user.address.city")
Array fieldregister(\items.${index}.name`)`
Flat array (v7)Not supported; use { value: ... } objects

v8 Beta Deltas

Patternv7v8
Field array keyfield.idfield.key
Array overwritesetValue("items", arr)replace(arr)
Watch callbackwatch((v) => ...)subscribe({ formState: { values: true }, callback })
Method refsStableNew ref on state change; remove from effect deps
Callback paramsanyunknown; annotate types
Nested errorsformState from useFormContext()useFormState({ control })
<Watch /> propnamesname
Flat field arraysNot supportedSupported (primitive rows)

Was this helpful?


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!