In React Hook Form, what do the useForm options mode and reValidateMode control, and what are their defaults?
answer
- before versus after the first submit
- onSubmit then onChange
- onTouched and all
- resolver runs at the same moments
basics
~20 sIn React Hook Form, mode sets when fields validate before the first submit (default 'onSubmit'); reValidateMode sets when they validate after a submit (default 'onChange'). By default nothing validates while typing until the user has submitted once.
solid answer
~40 s`mode` accepts `'onSubmit' | 'onBlur' | 'onChange' | 'onTouched' | 'all'` and defaults to `'onSubmit'`: before the first submit, only `handleSubmit` validates. `reValidateMode` accepts `'onChange' | 'onBlur' | 'onSubmit'` and defaults to `'onChange'`: once the form has been submitted, fields re-validate on change, so an error disappears as soon as the user fixes it. `'onTouched'` validates a field on its first blur and on every change after that; `'all'` validates on both blur and change and ignores `reValidateMode`. A **resolver** such as `zodResolver(schema)` does not change *when* validation runs — it replaces *what* runs, and it cannot be combined with `register`'s built-in rules. The documentation warns that `'onChange'` can cost many re-renders.
code
tsx · 18 linesimport { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const schema = z.object({
email: z.string().min(1, 'Email is required'),
password: z.string().min(12, 'Use at least 12 characters'),
});
type Values = z.infer<typeof schema>;
export function useSignupForm() {
return useForm<Values>({
resolver: zodResolver(schema),
mode: 'onTouched', // before submit: first blur, then every change
reValidateMode: 'onChange', // after submit (the default, shown for clarity)
defaultValues: { email: '', password: '' },
});
}go deeper
Recall the two defaults, onSubmit and onChange, and which phase each one governs.
Explain every mode value, why onTouched is a common choice, and how a resolver slots into the same timing.
Pick a mode per form from its size and schema cost, and spot mixed register rules and resolvers in review.
Set a house default for validation timing across a product's forms and justify the exceptions.
## Two options, two phases React Hook Form splits a form's life into two phases, separated by the first call of the `handleSubmit` handler: - **Before the first submit**, the `mode` option decides which event validates a field. - **After a submit**, the `reValidateMode` option decides it. Both are passed to `useForm`. The split exists because the two phases want different feels: before submitting, a user filling in a blank form should not be scolded mid-word; after a failed submit, the user is fixing known errors and wants each to clear the moment it is fixed. ## The values and their defaults | Option | Allowed values | Default | |---|---|---| | `mode` | `onSubmit`, `onBlur`, `onChange`, `onTouched`, `all` | `onSubmit` | | `reValidateMode` | `onChange`, `onBlur`, `onSubmit` | `onChange` | What each `mode` does before the first submit: - **`onSubmit`** — nothing validates until `handleSubmit` runs. - **`onBlur`** — a field validates when it loses focus. - **`onChange`** — a field validates on every change event. The documentation warns this can mean many re-renders and a significant performance cost. - **`onTouched`** — a field validates on its first blur, then on every change after that. With a `Controller`, you must wire `field.onBlur` for this to work. - **`all`** — validates on both blur and change, before and after submit; `reValidateMode` is ignored. So the default pair means: **silent until the first submit, then live on every change.** Since 7.56.0 both options are reactive — you can change them after the form is created and later validations follow the new strategy. ## Where resolvers fit A **resolver** is how RHF plugs in a schema library. You install `@hookform/resolvers` and pass, for example, `resolver: zodResolver(schema)`. Three facts matter: 1. The resolver runs **at the moments `mode` and `reValidateMode` choose**, and on submit. It does not have its own timing. 2. It receives the **whole form's values**, plus the names of the fields being validated; on an interaction, RHF updates the error only for the field being interacted with. 3. It **replaces** built-in validation: the documentation states a resolver cannot be used with built-in validators such as `required` or `min` on `register`. Put every rule in the schema. ## Choosing a mode - A long form where users type a lot: keep `onSubmit` or use `onTouched`, which waits for the first blur and then gives live feedback. - A short form with a strict format, like a coupon code: `onChange` is acceptable because re-render cost is small. - A form using a heavy schema: prefer `onBlur` or `onTouched`, because `onChange` runs the resolver on every keystroke. - `all` is rarely right; it is noisy before the user has finished a field. ```tsx const form = useForm<Values>({ resolver: zodResolver(schema), mode: 'onTouched', // first blur, then every change reValidateMode: 'onChange', defaultValues, }); ``` ## Common mistakes - Believing `mode: 'onSubmit'` means errors only ever update on submit; after the first submit, `reValidateMode` takes over and the default re-validates on change. - Mixing `required` on `register` with a resolver and expecting both to run. - Choosing `onChange` by default on a large form and then blaming the library for lag.
- With React Hook Form's default mode and reValidateMode, a user fixes an email after a failed submit. When does the error clear?On the next change event in that field. The form has been submitted, so `reValidateMode` (default `'onChange'`) applies, and the field re-validates as the user types. Before the first submit, the same typing would have validated nothing under the default `mode: 'onSubmit'`.
- In React Hook Form, how do you re-validate confirmPassword whenever password changes?Add `deps` to the field whose change should trigger the other: `register('password', { deps: ['confirmPassword'] })`. Whenever `password` validates through its own change or blur events, `confirmPassword` is validated too. Calling `trigger('confirmPassword')` yourself also works, but note that `trigger()` does not honour `deps`.
saying these in an interview costs you the question
- React Hook Form's default mode validates on every keystroke
- With mode 'onSubmit', errors update only when the form is submitted again
- reValidateMode also applies before the first submit
- register's required rule still runs alongside a zodResolver
- A resolver validates on its own schedule, independent of mode