useForm: Function
By invoking useForm
, you will receive the following methods register, unregister, errors, watch, handleSubmit, reset, setError, clearError, setValue, getValues, triggerValidation, control and formState.
useForm
also has optional arguments. The following example demonstrates all options' default value.
const { register } = useForm({
mode: 'onSubmit',
reValidateMode: 'onChange',
defaultValues: {},
validationSchema: {},
validateCriteriaMode: "firstErrorDetected",
submitFocusError: true,
nativeValidation: false,
})
mode: string = 'onSubmit'
React Native: not compatible (DOM API only)
Name | Type | Description |
---|---|---|
onSubmit (Default) | 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. Not recommended: Consider this as a bad performance practice. |
defaultValues: Record<string, any> = {}
React Native: Custom register or using Controller
You can set the input's default value with defaultValue/defaultChecked
(read more from the React doc for Default Values) or pass defaultValues
as an optional argument to populate default values for the entire form.
Note: Values defined in defaultValues
will be injected into watch as defaultValue
.
Note: defaultValues
doesn't auto populate with the manually registered input (eg: register({ name: 'test' })
) because the manual register
field does not provide the ref
to React Hook Form.
const { register } = useForm({
defaultValues: {
firstName: "bill",
lastName: "luo",
email: "bluebill1049@hotmail.com",
pets: [ 'dog', 'cat' ]
}
})
<input name="firstName" ref={register} /> // ✅ working version
<input name="lastName" ref={() => register({ name: 'lastName' })} />
// ❌ above example does not work with "defaultValues" due to its "ref" not being provided
Object | Apply form validation rules with |
firstErrorDetected | all | The default behavior With config set to |
onChange | onBlur | onSubmit | This option allows you to configure when inputs with errors getd re-validated (by default, validation is triggered during an input change.) React Native: not compatible (DOM API only) |
boolean = true | By default when the user submits a form and that contains an error, the first field with an error will be focused. Note: Only registered fields with a |
♦
register: (Ref, validateRule?) => voidReact Native: Custom register or using Controller
This method allows you to register input/select Ref
and validation rules into React Hook Form.
Validation rules are all based on HTML standard and also allow custom validation.
Important: name
is required and unique. Input name also supports dot and bracket syntax, which allows you to easily create nested form fields. Example table is below:
Input Name | Submit Result |
---|---|
name="firstName" | { firstName: 'value'} |
name="firstName[0]" | { firstName: [ 'value' ] } |
name="name.firstName" | { name: { firstName: 'value' } } |
name="name.firstName[0]" | { name: { firstName: [ 'value' ] } } |
If you working on arrays/array fields
, you can assign an input name as name[index]
. Check out the Field Array example.
Custom Register
You can also register inputs manually, which is useful when working with custom components and Ref
is not accessible. This is actually the case when you are working with React Native or custom component like react-select.
By using a custom register call, you will need to update the input value with setValue, because input is no longer registered with its ref.
register({ name: 'firstName' }, { required: true, min: 8 })
Note: If you want the custom registered input to trigger a re-render during its value update, then you should give a type to your registered input.
register({ name: 'firstName', type: 'custom' }, { required: true, min: 8 })
Name | Description | Code Examples |
---|---|---|
ref React.RefObject | React element ref |
|
required boolean | A Boolean which, if true, indicates that the input must have a value before the form can be submitted. You can assign a string to return an error message in the errors object. |
|
maxLength number | The maximum length of the value to accept for this input. |
|
minLength number | The minimum length of the value to accept for this input. |
|
max number | The maximum value to accept for this input. |
|
min number | The minimum value to accept for this input. |
|
pattern RegExp | The regex pattern for the input. |
|
validate Function | Object | You can pass a callback function as the argument to validate, or you can pass an object of callback functions to validate all of them. (refer to the examples) |
|
unregister: (name: string | string[]) => void
This method will allow you to unregister
a single input or an array of inputs. This is useful when you register your input during useEffect
as custom register and to unregister it after component unmount.
Note: When you unregister an input, its value will no longer be included in the form data that gets submitted.
import React from "react"
import { useForm } from "react-hook-form"
export default function App() {
const { register, handleSubmit, unregister } = useForm();
const onSubmit = (data) => console.log(data);
useEffect(() => {
register({ name: 'customRegister' }, { required: true });
return () => unregister('customRegister'); // unregister input after component unmount
}, [register])
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input type="text" name="firstName" ref={register} />
<input type="text" name="lastName" ref={register} />
<button type="button" onClick={() => unregister('lastName')}>unregister</button>
<input type="submit" />
</form>
);
}
errors: Record<string, Object>
Object contains form errors or error messages which belong to each input.
Note: Difference between V3 and V4:
V4: Nested object
Reason: as optional chaining becoming more popular among the community and to support better type.
errors?.yourDetail?.firstName;
V3: Flatten object
Reason: simple and easy to access error.
errors['yourDetail.firstName'];
Name | Type | Description |
---|---|---|
type | string | Error Type. eg: required, min, max, minLength |
types | Record<{ string, string | boolean }> | This is useful for input validation like rules of password, which multiple errors need to return for a single field. To enable this feature, make sure you have set validateCriteriaMode: 'all' . |
message | string | Message is an empty string by default. However, if you register validation with error message, then it will be returned. |
ref | React.RefObject | Reference for your input element. |
import React from "react";
import { useForm } from "react-hook-form";
export default function App() {
const { register, errors, handleSubmit } = useForm();
const onSubmit = data => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input name="singleErrorInput" ref={register({ required: true })} />
{errors.singleErrorInput && "Your input is required"}
{/* refer to the type of error to display message accordingly */}
<input
name="multipleErrorInput"
ref={register({ required: true, maxLength: 50 })}
/>
{errors.multipleErrorInput &&
errors.multipleErrorInput.type === "required" &&
"Your input is required"}
{errors.multipleErrorInput &&
errors.multipleErrorInput.type === "maxLength" &&
"Your input exceed maxLength"}
{/* register with validation */}
<input type="number" name="numberInput" ref={register({ min: 50 })} />
{errors.numberInput && "Your input required to be more than 50"}
{/* register with validation and error message */}
<input
name="errorMessage"
ref={register({ required: "This is required" })}
/>
{errors.errorMessage && errors.errorMessage.message}
<input type="submit" />
</form>
);
}
watch: (names?: string | string[] | { nest : boolean }) => any
This will watch specified input/inputs and return its value, and it's useful for determining what to render.
When
defaultValue
is not defined, the first render ofwatch
will returnundefined
because it is called beforeregister
, but you can set thedefaultValue
as the second argument to return value.However, if
defaultValues
was initialised inuseForm
as argument, then the first render will return what's provided indefaultValues
.
Type | Description | Example | Return |
---|---|---|---|
string | Watch input value by name (similar to lodash get function) | watch('inputName') watch('inputName', 'value') | string | string[] | { [key:string] : any } | undefined |
string[] | Watch multiple inputs | watch(['inputName1']) watch(['field1'], { field1: '1' }) | { [key:string] : any } |
undefined | Watch all inputs | watch() watch(undefined, { field: 'value1' }) | { [key:string] : any } |
{ nest: boolean } | Watch all inputs and return nested object | watch({ nest: true }) | { [key:string] : any } |
import React from 'react';
import { useForm } from 'react-hook-form';
export default function App(props) {
const { register, watch } = useForm();
const watchYes = watch('yes', props.yes); // supply default value as second argument
const watchAllFields = watch(); // watching every fields in the form
const watchFields = watch(['yes', 'number']); // target specific fields by their names
// watch array fields by the key, pet[0] and pet[1] will both be watched and returns values
const pets = watch('pet');
return (
<form>
<input name="textInput" ref={register({ required: true, maxLength: 50 })} />
<input type="checkbox" name="yes" ref={register} />
<input name="pet[0]" ref={register} />
<input name="pet[1]" ref={register} />
{watchYes && <input type="number" name="numberInput" ref={register({ min: 50 })} />}
{/* based on yes selection to display numberInput */}
</form>
);
}
handleSubmit: (data: Object, e: Event) => void
This function will pass the form data when form validation is successful.
Note: You can pass an async
function for asynchronous validation. eg:
handleSubmit(async (data) => await fetchAPI(data))
import React from "react";
import { useForm } from "react-hook-form";
export default function App() {
const { register, handleSubmit } = useForm()
const onSubmit = (data, e) => {
console.log('Submit event', e)
alert(JSON.stringify(data))
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input name="firstName" ref={register} />
<input name="lastName" ref={register} />
<button type="submit">Submit</button>
</form>
)
}
reset: (values?: Record<string, any>) => void
This function will reset the fields' values and errors within the form. You can pass values
as an optional argument to reset your form into assigned default values.
Note: For controlled components like React-Select
which don't expose ref
, you will have to reset the input value manually through setValue or using Controller to wrap around your controlled component.
import React from "react";
import { useForm } from "react-hook-form";
export default function App() {
const { register, handleSubmit, reset } = useForm();
const onSubmit = (data, e) => {
// e.target.reset();
// you can use HTML standard reset() function, but it only reset inputs' value
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input name="firstName" ref={register({ required: true })} />
<input name="lastName" ref={register} />
<input type="submit" />
<input type="reset" /> // standard reset button
<input type="button" onClick={reset} />
<input
type="button"
onClick={() => {
reset({
firstName: "bill",
lastName: "luo"
});
}}
/> // reset form with values
</form>
);
}
setError:
(name: string | ManualFieldError[], type?: string | Object, message?: string) => void
The function allows you to manually set one or multiple errors.
import React from "react";
import { useForm } from "react-hook-form";
export default function App() {
const { register, errors, setError } = useForm()
return (
<form>
<input
name="username"
onChange={e => {
const value = e.target.value
// this will clear error by only pass the name of field
if (value === "bill") return clearError("username")
// set an error with type and message
setError("username", "notMatch", "please choose a different username")
}}
ref={register}
/>
{errors.username && errors.username.message}
</form>
)
}
clearError: (name?: string | string[]) => void
undefined
: reset all errorsstring
: reset single errorstring[]
: reset multiple errors
import React from "react";
import { useForm } from "react-hook-form";
export default () => {
const { clearError, errors, register } = useForm();
return (
<form>
<input name="firstName" ref={register({ required: true })} />
{errors.firstName && "This is required"}
<input name="lastName" ref={register({ required: true })} />
{errors.lastName && "This is required"}
<button type="button" onClick={() => clearError("firstName")}>
Clear
</button>
<button
type="button"
onClick={() => clearError(["firstName", "lastName"])}
>
Clear Multiple
</button>
<button type="button" onClick={() => clearError()}>
Clear All
</button>
</form>
);
};
setValue: (name: string, value: any, shouldValidate?: boolean) => void
This function allows you to dynamically set input/select value. At the same time, it tries to avoid re-render when it's not necessary and only the following conditions will trigger re-render.
When an error is triggered by a value update
When an error is corrected by a value update
When setValue is invoked for the first time and formState
dirty
is set to trueWhen setValue is invoked and formState
touched
is updated
Note: By invoking this method, formState
will push the input's name
into touched
.
You can also set shouldValidate
to true
and it will trigger field validation. eg: setValue('name', 'value', true)
import React from "react"
import { useForm } from "react-hook-form"
export default function App() {
const { register, setValue } = useForm()
return (
<form>
<input name="test" ref={register} />
<button type="button" onClick={() => {
// manually set the 'test' field with value 'bill'
setValue('test', 'bill')
}}>SetValue</button>
</form>
)
}
getValues: (payload?: { nest: boolean }) => Object
This function will return the entire form data, and it's useful in a function when you want to retrieve form values.
By default
getValues()
will return form data in a flat structure. eg:{ test: 'data', test1: 'data1'}
Working on the defined form fields,
getValues({ nest: true })
will return data in a nested structure according to inputname
. eg:{ test: [1, 2], test1: { data: '23' } }
import React from "react";
import { useForm } from "react-hook-form";
export default function App() {
const { register, getValues } = useForm()
return (
<form>
<input name="test" ref={register} />
<input name="test1" ref={register} />
<button
type="button"
onClick={() => {
const values = getValues()
// you can run auto save function here eg: autoSave(values)
}}
>
Get Values
</button>
</form>
);
}
triggerValidation: (payload?: string | string[]) => Promise<boolean>
To manually trigger an input/select validation in the form.
Note: When validation fails, the errors
object will be updated.
import React from "react";
import { useForm } from "react-hook-form";
export default function App() {
const { register, triggerValidation, errors } = useForm();
console.log(errors);
return (
<form>
<input name="lastName" ref={register({ required: true })} />
<button
type="button"
onClick={async () => {
triggerValidation("lastName");
}}
>
Trigger
</button>
</form>
);
}
control: Object
This object is made for React Hook Form's Controller component, which contains methods for registering controlled component into React Hook Form.
import React from "react";
import { useForm, Controller } from "react-hook-form";
import { TextField } from "@material-ui/core";
function App() {
const { control, handleSubmit } = useForm();
const onSubmit = data => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
as={<TextField />}
name="firstName"
control={control}
defaultValue=""
/>
<input type="submit" />
</form>
);
}
formState: Object
This object contain information about the form state.
Name | Type | Description |
---|---|---|
dirty | boolean | Set to true after a user interacted with any of the inputs. |
isSubmitted | boolean | Set true after a user submitted the form. |
touched | object | An object of all inputs which have been interacted. |
isSubmitting | boolean | During form submitting will set to true and after submitting set to false |
submitCount | number | Number of forms submit. |
isValid | boolean | Set true if doesn't have any error. |
CodeSandbox |
FormContext: Component
Form context is intended to solve the problem when there are deep nested inputs in your components tree, and passing methods deep down as props
becomes painful.
Name | Type | Description |
---|---|---|
...props | Object | Accept all useForm methods. |
Once your form is wrapped with FormContext
, the useFormContext
: function can be invoked in its child component.
Note: invoking useFormContext
will give you all of the useForm
hook functions.
const methods = useFormContext() // methods contain all useForm functions
import React from "react"
import { useForm, FormContext, useFormContext } from "react-hook-form"
export default function App() {
const methods = useForm()
const onSubmit = data => { console.log(data) }
return (
<FormContext {...methods} > // pass all methods into the context
<form onSubmit={methods.handleSubmit(onSubmit)}>
<NestedInput />
<input type="submit" />
</form>
</FormContext>
)
}
function NestedInput() {
const { register } = useFormContext() // retrieve all hook methods
return <input name="test" ref={register} />
}
Controller: Component
React Hook Form embrace uncontrolled components and native inputs, however it's hard to avoid working with external controlled component such as React-Select, AntD and Material-UI. This wrapper component will make your life easier to work with them.
Every props you pass to Controller component, will be forwarded to the Component instance you provided with the as
prop. That means imagine you have a custom Switch
component that require a label
prop. You can pass this prop to the Controller component directly. The name
prop will be used mainly to access the value through the form later.
If you use a defaultValue
prop, it will take priority over the useForm
defaultValues
value for your property given at name
prop.
Name | Type | Required | Description |
---|---|---|---|
name | string | ✓ | Unique name of your input. |
as | React.ElementType | string | ✓ | Controlled component. eg: as="input" or as={<TextInput />} |
control | Object | control object is from invoking useForm . it's optional if you are using FormContext. | |
defaultValue | any | The same as uncontrolled component's defaultValue , when supply boolean value, it will be treated as checkbox input.Note: you will need to supply either Note: When provided, this take priority over | |
rules | Object | Validation rules according to register . | |
onChange | (args: any) => any | This onChange prop allow you to customise the return value. eg: onChange={{(data) => data.value}} | |
onBlur | (args: any) => any | This onBlur prop allow you to customise the return value. eg: onBlur={{(data) => data.value}} | |
onChangeName | string | This prop allow you to target that specific event name, eg: when onChange event is named onTextChange | |
onBlurName | string | This prop allow you to target that specific event name, eg: when onBlur event is named onTextBlur | |
valueName | string | This prop allows you to override the value prop and support other components which doesn't use value prop. eg: checked , selected and ect... |
import React from 'react';
import Select from 'react-select';
import { TextField } from "@material-ui/core";
import { useForm, Controller } from 'react-hook-form';
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' },
];
function App() {
const { handleSubmit, control } = useForm();
return (
<form onSubmit={handleSubmit(data => console.log(data))}>
{* // Preferred syntax on most cases. If you need props, pass TextField props to Controller props (forwarded) *}
<Controller as={TextField} name="TextField" control={control} defaultValue="" />
{* // Another possibility, any potential props passed to <Checkbox/> will be overrided. SomeName => Checkbox *}
<Controller
as={<Select options={options} />}
control={control}
rules={{ required: true }}
onChange={([selected]) => {
// React Select return object instead of value for selection
return { value: selected };
}}
name="reactSelect"
defaultValue={{ value: 'chocolate' }}
/>
<Controller
as={<TextField />}
name="firstName"
control={control}
defaultValue=""
/>
<button>submit</button>
</form>
);
}
ErrorMessage: Component
A simple component to render associated input's error message.
Name | Type | Required | Description |
---|---|---|---|
name | string | ✓ | associated field name. |
errors | object | errors object from React Hook Form. it's optional if you are using FormContext. | |
as | React.ElementType | string | Wrapper component or HTML tag. eg: as="span" or as={<Text />} | |
children | ({ message: string, messages: string[]}) => any | This is a render prop for rendering error message or messages. |
import React from "react";
import { useForm, ErrorMessage } from "react-hook-form";
export default function App() {
const { register, errors, handleSubmit } = useForm();
const onSubmit = data => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input name="singleErrorInput" ref={register({ required: true })} />
<ErrorMessage errors={errors} name="singleErrorInput" />
<input type="submit" />
</form>
);
}
validationSchema: Object
If you would like to centralize your validation rules with external validation schema, you can apply validationSchema
at useForm
as an optional argument. React Hook Form currently supports Yup for object schema validation.
import React from 'react'
import ReactDOM from 'react-dom'
import { useForm } from 'react-hook-form'
import * as yup from 'yup'
const SignupSchema = yup.object().shape({
name: yup.string().required(),
age: yup.number().required(),
})
export default function App() {
const { register, handleSubmit, errors } = useForm({
validationSchema: SignupSchema
})
const onSubmit = data => { console.log(data); }
console.log(errors)
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input name="name" ref={register} />
<input type="number" name="age" ref={register} />
<input type="submit" />
</form>
)
}
Browser built-in validation (V3 only)
The following example demonstrates how you can leverage the browser's validation. You only need to set nativeValidation
to true
and the rest of the syntax is the same as standard validation.
Note: This feature has been removed in V4 due to low usage, but you can still using it in V3
import React from "react";
import { useForm } from "react-hook-form";
export default function App() {
const { register, handleSubmit } = useForm({ nativeValidation: true });
const onSubmit = async data => { alert(JSON.stringify(data)); };
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input
name="firstName"
ref={register({ required: "Please enter your first name." })} // custom message
/>
<input name="lastName" ref={register({ required: true })} />
<input type="submit" />
</form>
);
}
Advanced Usage
Learn how to build complex and accessible forms with React Hook Form.