Skip to content

API

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)
NameTypeDescription
onSubmit (Default)stringValidation will trigger on the submit event and invalid inputs will attach onChange event listeners to re-validate them.
onBlurstringValidation will trigger on the blur event.
onChangestringValidation 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.

CodeSandbox
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
validationSchema:
Object

Apply form validation rules with Yup at the schema level, please refer to the validationSchema section.

CodeSandbox
validateCriteriaMode:
firstErrorDetected | all

The default behavior firstErrorDetected will validate all fields' validation and gather all the first error encounter.

With config set to all, all fields' validation will be run through and gather all the errors encounter.

CodeSandbox
reValidateMode:
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)

submitFocusError:
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 ref will work. Manually registered inputs won't work. eg: register({ name: 'test' }) // doesn't work

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 NameSubmit 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 })

Register options

By selecting the register option, the API table below will get updated.

NameDescriptionCode Examples
ref
React.RefObject
React element ref
<input
  name="test"
  ref={register}
/>
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.
<input
  name="test"
  ref={
    register({
      required: true
    })
  }
/>
maxLength
number
The maximum length of the value to accept for this input.
<input
  name="test"
  ref={
    register({
      maxLength: 2
    })
  }
/>
minLength
number
The minimum length of the value to accept for this input.
<input
  name="test"
  ref={
    register({
      minLength: 1
    })
  }
/>
max
number
The maximum value to accept for this input.
<input
  name="test"
  ref={
    register({
      max: 3
    })
  }
/>
min
number
The minimum value to accept for this input.
<input
  name="test"
  ref={
    register({
      min: 3
    })
  }
/>
pattern
RegExp
The regex pattern for the input.
<input
  name="test"
  ref={
    register({
      pattern: /[A-Za-z]{3}/
    })
  }
/>
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)
// callback function
<input
  name="test"
  ref={
    register({
      validate: value => value === '1'
    })
  }
/>
// object of callback functions
<input
  name="test1"
  ref={
    register({
      validate: {
        positive: value => parseInt(value) > 0,
        lessThanTen: value => parseInt(value) < 10
      }
    })
  }
/>
// you can do asynchronous validation as well
<input
  name="test2"
  ref={
    register({
      validate: async value => await fetch(url)
    })
  }
/>

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.

CodeSandbox
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: The object itself is structured as flat (not nested) which means you access errors directly with input name. eg:

<input name="username[0].firstName" ref={register} />; errors['username[0].firstName'];

we will consider to introduce nested error object once Optional chaining has better browser support.

Note: You can also learn recipes on Error Message from advanced page.

NameTypeDescription
typestringError Type. eg: required, min, max, minLength
typesRecord<{ 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'.
messagestringMessage is an empty string by default. However, if you register validation with error message, then it will be returned.
refReact.RefObjectReference for your input element.
CodeSandbox
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 of watch will return undefined because it is called before register, but you can set the defaultValue as the second argument to return value.

  • However, if defaultValues was initialised in useForm as argument, then the first render will return what's provided in defaultValues.

TypeDescriptionExampleReturn
stringWatch input value by name (similar to lodash get function)watch('inputName')
watch('inputName', 'value')
string | string[] | { [key:string] : any } | undefined
string[]Watch multiple inputswatch(['inputName1'])
watch(['field1'], { field1: '1' })
{ [key:string] : any }
undefinedWatch all inputswatch()
watch(undefined, { field: 'value1' })
{ [key:string] : any }
{ nest: boolean }Watch all inputs and return nested objectwatch({ nest: true }){ [key:string] : any }
CodeSandbox
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))

CodeSandbox
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.

CodeSandbox
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.

CodeSandbox
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 errors

  • string: reset single error

  • string[]: 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 true

  • When 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)

CodeSandbox
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 input name. eg: { test: [1, 2], test1: { data: '23' } }

CodeSandbox
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?: { name: string } | { name: string }[]) => boolean

To manually trigger an input/select validation in the form.

Note: When validation fails, the errors object will be updated.

CodeSandbox
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="firstName" ref={register({ required: true })} />
      <input name="lastName" ref={register({ required: true })} />
      <button
        type="button"
        onClick={async () => {
          console.log("firstName validation result: ", await triggerValidation({ name: 'firstName' }))
         
          // you can trigger validation with value. It is useful for custom input when ref is not registered
          await triggerValidation({ name: 'lastName', value: 'test' })
          
          // you can trigger multiple fields validation
          await triggerValidation([{ name: 'lastName' }, { name: 'firstName' }])
          
          // you can trigger the entire form validation by supply empty argument
          await triggerValidation()
        }}
      >
        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.

CodeSandbox
import React from "react";
import { useForm, Controller } from "react-hook-form";
import { TextField } from "@material-ui/core";

function App() {
  const { register, setValue, handleSubmit } = useForm();

  const onSubmit = data => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Controller
        as={<TextField />}
        name="firstName"
        control={control}
      />
      
      <input type="submit" />
    </form>
  );
}

formState: Object

This object contain information about the form state.

NameTypeDescription
dirtybooleanSet to true after a user interacted with any of the inputs.
isSubmittedbooleanSet true after a user submitted the form.
touchedstring[]An array of all inputs which have been interacted.
isSubmittingbooleanDuring form submitting will set to true and after submitting set to false
submitCountnumberNumber of forms submit.
isValidbooleanSet 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.

NameTypeDescription
...propsObjectAccept 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
CodeSandbox
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.

NameTypeDescription
namestringUnique name of your input.
controlObjectcontrol object is from invoking useForm.
rulesObjectValidation rules according to register at React Hook Form
onChangeNamestringThis prop allow you to target that specific event name, eg: whenonChange event is named onTextChange
onBlurNamestringThis prop allow you to target that specific event name, eg: whenonBlur event is named onTextBlur
CodeSandbox
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))}>
      <Controller
        as={<Select options={options} />}
        control={control}
        rules={{ required: true }}
        name="reactSelect"
      />
      
      <Controller
        as={<TextField />}
        name="firstName"
        control={control}
      />
      
      <button>submit</button>
    </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.

CodeSandbox
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>
  )
}

Advanced Usage

Learn how to build complex and accessible forms with React Hook Form.