useForm: UseFormProps
useForm is a custom hook for managing forms with ease. It takes one object as optional argument. The following example demonstrates all of its properties along with their default values.
useForm({ mode: 'onSubmit', reValidateMode: 'onChange', defaultValues: {}, resolver: undefined, context: undefined, criteriaMode: "firstError", shouldFocusError: true, shouldUnregister: false, shouldUseNativeValidation: false, delayError: undefined })type FormInputs = { firstName: string; lastName: string; }; const { register } = useForm<FormInputs>({ mode: 'onSubmit', reValidateMode: 'onChange', defaultValues: {}, resolver: undefined, context: undefined, criteriaMode: "firstError", shouldFocusError: true, shouldUnregister: false, delayError: undefined })
Props
mode: onChange | onBlur | onSubmit | onTouched | all = 'onSubmit'React Native: compatible with Controller
This option allows you to configure the validation strategy before a user submits the form (onSubmit event).
| Name | Type | Description |
|---|---|---|
| onSubmit | string | Validation will trigger on the submit event and invalid inputs will attach onChange event listeners to re-validate them. |
| onBlur | string | Validation will trigger on the blur event. |
| onChange | string | Validation will trigger on the change event with each input, and lead to multiple re-renders. Warning: this often comes with a significant impact on performance. |
| onTouched | string | Validation will trigger on the first Note: when using with |
| all | string | Validation will trigger on the blur and change events. |
reValidateMode: onChange | onBlur | onSubmit = 'onChange'React Native: Custom register or using Controller
This option allows you to configure validation strategy when inputs with errors get re-validated after a user submits the form (onSubmit event). By default, validation is triggered during the input change event.
defaultValues: Record<string, any> = {}
The defaultValues for inputs are used as the initial value when a component is first rendered, before a user interacts with it. It is encouraged that you set defaultValues for all inputs to non-undefined values such as the empty string or null.
You can set an input's default value with defaultValue/defaultChecked (read more from the React doc for Default Values). You can pass defaultValues as an optional argument to useForm() to populate the default values for the entire form, or set values on an individual Controller component via its defaultValue property. If both defaultValue and defaultValues are set, the value from defaultValues will be used.
Rules
Important: You should provide a proper default value and avoid
undefined.undefined is reserved for fallback from inline
defaultValue/defaultCheckedto hook leveldefaultValues.undefinedvalue is conflicting with controlled component as default state
defaultValuesare cached on the first render within the custom hook. If you want to reset thedefaultValues, you should use the reset api.defaultValueswill be injected into watch, useWatch, Controller and useController'sdefaultValue.defaultValueswill be included in the submission result by default, if this is not the desired behavior useshouldUnregister: trueinstead which means input values will host within all the fields.It's recommend to avoid including custom object which contains prototype methods as the
defaultValues, such asmoment,luxonand etc. Those type of an object will not be cloned internally.There are other options to include form data:
// include hidden input <input {...register("hidden")} type="hidden" /> // register input with value register("hidden", { value: "data" }) // include data onSubmit const onSubmit = (data) => { const output = { ...data, others: "others" } }
| This context | CodeSandbox |
|
| CodeSandbox |
| When set to true (default) and the user submits a form that fails the validation, it will set focus on the first field with an error. Note: only registered fields with a Note: the focus order is based on the | |
| This config will delay the error state to be displayed to the end-user in milliseconds. Correct the error input will remove the error instantly and delay will not be applied. | CodeSandbox |
shouldUnregister: boolean = false
By default, an input value will be retained when input is removed. However, you can set shouldUnregister to true to unregister input during unmount.
This is a global config that overwrites child-level config, if you want to have individual behavior, then you should set the config at the component or hook level, not at
useForm.By default
shouldUnregister: false: unmounted fields will not be validated by build-in validation.By setting
shouldUnregisterto true atuseFormlevel,defaultValueswill not be merged against submission result.set
shouldUnregister: truewill set your form behave more closer as native.Form values will be lived inside your inputs itself.
input unmount will remove value.
input hidden should be applied for hidden data.
only registered input will be included as submission data.
unmounted input will need to notify at either
useForm, oruseWatch'suseEffectfor hook form to verify input is unmounted from the DOM.const NotWork = () => { const [show, setShow] = React.useState(false); // ❌ won't get notified, need to invoke unregister return {show && <input {...register('test')} />} } const Work = () => { const { show } = useWatch() // ✅ get notified at useEffect return {show && <input {...register('test1')} />} } const App = () => { const [show, setShow] = React.useState(false); const { control } = useForm({ shouldUnregister: true }); return ( <div> // ✅ get notified at useForm's useEffect {show && <input {...register('test2')} />} <NotWork /> <Work control={control} /> </div> ) }
shouldUseNativeValidation: boolean = false
This config will enable browser native validation. It will also enable CSS selectors :valid and:invalid making style inputs easier. In fact, you can still use those selectors even the client validation is disabled.
You can turn on this config and set
novalidateat your form and still use those CSS selectors.This feature only works for
registerAPI, notuseController/Controller.
Examples
import { useForm } from "react-hook-form"; export default function App() { const { register, handleSubmit } = useForm({ shouldUseNativeValidation: true }); const onSubmit = async data => { console.log(data); }; return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register("firstName", { required: "Please enter your first name." })} // custom message /> <input type="submit" /> </form> ); }
resolver: Resolver
This function allows you to use any external validation library such as Yup, Zod, Joi, Vest, Ajv and many others. The goal is to make sure you can seamlessly integrate whichever validation library you prefer. If you're not using a library, you can always write your own logic to validate your forms.
npm install @hookform/resolversProps
| Name | Type | Description |
|---|---|---|
| object | This object contains the entire form values. |
| object | This is the |
| {
criteriaMode: string,
fields: object,
names: string[]
} | This is the option object contains information about the validated fields, names and |
Rules
Schema validation focus on the field level for error reporting. Parent level error look is only limited to the direct parent level that is applicable for components such as group checkboxes.
This function will be cached.
Re-validation of an input will only occur one field at time during a user’s interaction. The lib itself will evaluate the
errorobject to trigger a re-render accordingly.A resolver cannot be used with the built-in validators (e.g.: required, min, etc.)
When building a custom resolver:
Make sure you are returning an object that has both a
valuesand anerrorsproperty. Their default values should be an empty object. For example:{}.The keys of the
errorobject should match thenamevalues of your fields.
Examples
import React from 'react'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import * as yup from "yup"; const schema = yup.object().shape({ name: yup.string().required(), age: yup.number().required(), }).required(); const App = () => { const { register, handleSubmit } = useForm({ resolver: yupResolver(schema), }); return ( <form onSubmit={handleSubmit(d => console.log(d))}> <input {...register("name")} /> <input type="number" {...register("age")} /> <input type="submit" /> </form> ); };import React from 'react'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import * as yup from "yup"; type Inputs = { name: string; age: string; }; const schema = yup.object().shape({ name: yup.string().required(), age: yup.number().required(), }).required(); const App = () => { const { register, handleSubmit } = useForm<Inputs>({ resolver: yupResolver(schema), // yup, joi and even your own. }); return ( <form onSubmit={handleSubmit(d => console.log(d))}> <input {...register("name")} /> <input type="number" {...register("age")} /> <input type="submit" /> </form> ); };
Need more? See Resolver Documentation
Tip
You can debug your schema via the following code snippet:
resolver: async (data, context, options) => { // you can debug your validation schema here console.log('formData', data) console.log('validation result', await anyResolver(schema)(data, context, options)) return anyResolver(schema)(data, context, options) },
Return
The following list contains reference to useForm return props.
Thank you for your support
If you find React Hook Form to be useful in your project, please consider to star and support it.