useForm: Function
useForm
is custom hook for managing forms with ease. It takes optional arguments. The following example demonstrates all of the arguments 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 })
mode: onChange | onBlur | onSubmit | onTouched | all = 'onSubmit'
React Native: compatible with Controller
This option allows you to configure the validation before 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 when inputs with errors get re-validated after submit. By default, validation is only triggered during an input change.
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
defaultValues
are cached on the first render within the custom hook. If you want to reset thedefaultValues
, you should use the reset api.defaultValues
will be injected into watch, useWatch, Controller and useController'sdefaultValue
.It's not default state for the form, to include additional form values. To do so:
Register hidden inputs. For example:
<input type="hidden" {...register('test')} />
Combine values via the onSubmit callback.
Register an input with a value. For example:
register('test')({ test: "test"})
defaultValues
will be shallowly merged with form submission data.const { register } = useForm({ defaultValues: { firstName: "bill", lastName: "luo", } }) <input {...register("firstName")} /> // ✅ working version <Controller name="lastName" render={({ field }) => <input {...field} />} /> // ✅ working version
type Inputs = { firstName: string; lastName: string; email: string; isDeveloper: boolean; } const { register } = useForm<Inputs>({ defaultValues: { firstName: "bill", lastName: "luo", email: "bluebill1049@hotmail.com", isDeveloper: true } }) <input {...register("firstName")} /> // ✅ working version
From version 7.6.0 onwards with
shouldUnregister
set to false, any missing registered inputs fromdefaultValues
will get automatically registered. However, it's still recommend using theregister
method and provide hidden input to follow HTML standard.Important: each object key will be
register
as an input.const App = () => { const { register, handleSubmit } = useForm({ defaultValues: { // missing "test.lastName" input will be registered test: { firstName: 'bill', lastName: 'luo' }, test1: ['test', 'test1'] }, }); // Inputs will get register via defaultValues // register('test.lastName') // register('test1.0') // register('test1.1') const onSubmit = (data) => console.log(data); // { test: { firstName: 'bill', lastName: 'luo' } }; // ✅ alternative custom register // useEffect(() => { // register('test.lastName'); // }, []) return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register('test.firstName')} /> // <input {...register('test.lastName')} type="hidden" /> ✅ alternative hidden input <button /> </form> ); };
| 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 | |
| By default, an input value will be retained when input is removed. However, you can set
| |
| 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 |
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.
Note: You can turn on this config and set
novalidate
at your form and still use those CSS selectors.Note: This feature only works for
register
API, notuseController/Controller
.
import React from "react"; import { useForm } from "react-hook-form"; export default function App() { const { register, handleSubmit } = useForm({ shouldUseNativeValidation: true }); const onSubmit = async data => { console.log(data); }; // you can still disabled the native validation, CSS selectors such as // invalid and valid still going to work // <form onSubmit={handleSubmit(onSubmit)} novalidate> return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register("firstName", { required: "Please enter your first name." })} // custom message /> <input type="submit" /> </form> ); }
resolver: (values: any, context?: object, options: Object) => Promise<ResolverResult> | ResolverResult
This function allows you to use any external validation library such as Yup, Zod, Joi, Superstruct, Vest 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/resolvers
Rules
Make sure you are returning an object that has both a
values
and anerrors
property. Their default values should be an empty object. For example:{}
.The keys of the
error
object should match thename
values of your fields.This function will be cached, while
context
is a mutableobject
that can be changed on each re-render.Re-validation of an input will only occur one field at time during a user’s interaction. The lib itself will evaluate the
error
object to trigger a re-render accordingly.A resolver cannot be used with the built-in validators (e.g.: required, min, etc.)
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 |
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(), }); 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(), }); 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> ); };