Immediate Fix
The most common cause of flickering in React Hook Form is the default validation mode. By default, many developers trigger validation on every keystroke, causing the UI to jump as error messages toggle.
To stop the flickering instantly, switch your useForm configuration from onChange to onTouched or onBlur. This prevents the state from updating and re-rendering the layout until the user moves to the next field.
const { register, handleSubmit } = useForm({
mode: 'onTouched',
reValidateMode: 'onBlur',
shouldUnregister: false
});
| Mode | Effect | Flicker Risk |
|---|---|---|
| onChange | Validates on every key press | High |
| onBlur | Validates when leaving input | Low |
| onTouched | Validates after first interaction | Medium |
Technical Explanation
Flickering occurs during the React reconciliation process. When React Hook Form updates its internal state to reflect a validation error, it triggers a re-render of the component tree.
If your form layout depends on conditional rendering—such as showing an error message that shifts other elements down—the rapid mounting and unmounting of these elements creates a visible “stutter” or flicker.
Another technical cause is the use of asynchronous defaultValues. If you are fetching data and updating the form using reset() inside a useEffect, the form might render once with empty values and then a second time with the fetched data, causing a flash of empty content.

Alternative Methods
1. Use the Controller Component
For complex UI libraries like Material UI or Ant Design, wrap your inputs in the Controller component. This provides a more stable bridge between the external input state and React Hook Form, reducing unnecessary parent re-renders.
<Controller
name="firstName"
control={control}
render={({ field }) => <Input {...field} />}
/>
2. Persistent Error Layouts
Instead of conditionally rendering error messages, keep the error container in the DOM with a fixed height. Use CSS visibility or opacity to show the text. This prevents the layout from shifting when the error appears.
/* CSS to prevent layout shift */
.error-message {
min-height: 20px;
visibility: hidden;
}
.error-message.visible {
visibility: visible;
}
3. Memoize Form Inputs
If your form is large, use React.memo on individual input components. This ensures that only the input currently being typed in re-renders, while the rest of the form remains static, eliminating the global screen flicker.