Star

API

React Hook Form focuses on providing the best DX by simplifying the API.

useForm: Function

By invoking useForm, you will receive the following methods register, unregister, errors, watch, handleSubmit, reset, setError, setValue, getValues and formState.

useForm also has optional arguments. The following example demonstrate all options' default value.

const { register } = useForm({
  mode: 'onSubmit',
  defaultValues: {},
  validationFields: [],
  validationSchema: {},
  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: {[key:string]: string} = {}React Native: Need to combine the use of watch API or simply use defaultValue

You can set input's default value with defaultValue/defaultChecked (read more at 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 manually registered input (eg:register({ name: 'test' })) because manual register field is not providing 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
validationFields: string[] = []

Providing an array of fields means only included fields will be validated. This option is useful when you want to toggle which fields are required to validate.

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

validationSchema: Object

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

CodeSandbox
nativeValidation: boolean = false

Setting this option to true will enable browser's native validation. You can find out more about the built-in browser validation, and refer to the nativeValidation section for more detail and example.

CodeSandbox

register: ({ name: string } | Ref, validateRule?) => voidReact Native: custom register only

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 support dot and bracket syntax, which allow you to easily create nested or array 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 input name as name[index]. Check out the array fields example.


Register options

Note: You can also register input 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

register({ name: 'firstName' }, { required: true, min: 8 })

If you want custom register input to trigger re-render during value update, then you should give a type to your registered input.

register({ name: 'firstName', type: 'custom' }, { required: true, min: 8 })

NameTypeDescriptionExample
refReact.RefObjectReact element ref
<input
  name="test"
  ref={register}
/>
requiredbooleanA Boolean which, if true, indicates that the input must have a value before the form can be submitted. you can assign as string to return error message in the errors object.
<input
  name="test"
  ref={
    register({
      required: true
    })
  }
/>
maxLengthnumberThe maximum length of the value to accept for this input.
<input
  name="test"
  ref={
    register({
      maxLength: 2
    })
  }
/>
minLengthnumberThe minimum length of the value to accept for this input.
<input
  name="test"
  ref={
    register({
      minLength: 1
    })
  }
/>
maxnumberThe maximum value to accept for this input.
<input
  name="test"
  ref={
    register({
      max: 3
    })
  }
/>
minnumberThe minimum value to accept for this input.
<input
  name="test"
  ref={
    register({
      min: 3
    })
  }
/>
patternRegExpThe regex pattern for the input.
<input
  name="test"
  ref={
    register({
      pattern: /[A-Za-z]{3}/
    })
  }
/>
validateFunction | ObjectYou 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)
<input
  name="single"
  ref={
    register({
      validate: (value) => value === '1'
    })
  }
/>

unregister: (name: string | string[]) => void

This method will allow you to unregister a single or array of inputs.

CodeSandbox
import React from "react"
import useForm from "react-hook-form"

export default function YourForm() {
  const { register, handleSubmit, unregister } = useForm()
  const onSubmit = (data) => console.log(data)

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

Object contain form errors or error messages which belong to each input.

NameTypeDescription
typestringError types. eg: required, min, max, minLength
messagestringMessage is empty string by default. However, if you register validation with error message, then it will be returned.
refReact.RefObjectReference fo your input element.
CodeSandbox
import React from 'react'
import useForm from 'react-hook-form'

function YourForm() {
  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

This will watch specified input/inputs and return its value.

  • When defaultValue is not defined, first render watch will return undefined because 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 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 }
CodeSandbox
import React from 'react'
import useForm from 'react-hook-form'

export default function YourForm(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 YourForm() {
  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>) => voidReact Native: not compatible (DOM API only)

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.

CodeSandbox
import React from "react";
import useForm from "react-hook-form";

export default function YourForm() {
  const { register, handleSubmit, reset } = useForm();
  const onSubmit = (data, e) => {
    e.target.reset(); // standard reset after form submit
  };

  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, type: string, message?: string, ref?: Ref) => void

Allows you to manually set an input error.

clearError: (name?: string | string[]) => void

  • undefined: reset all errors

  • string: reset single error

  • string[]: reset multiple errors

CodeSandbox
import React from "react";
import useForm from "react-hook-form";

export default function YourForm() {
  const { register, errors, setError, clearError } = 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>
  )
}

setValue: (name: string, value: any, shouldValidate?: boolean) => void

This function allows you to dynamically set input/select value.

Note: By invoking this method, formState will push 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 YourForm() {
  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.

  • By default getValues() will return form data in a flat structure. eg: { test: 'data', test1: 'data1'}
  • Working on array fields form, 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 YourForm() {
  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; value?: any; } | { name: string; value?: any; }[]) => 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 YourForm() {
  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>
  )
}

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 aim to solve problem when there are deep nested inputs in your components tree, and pass methods deep down as prop becoming painful.

NameTypeDescription
...propsObjectAccept all useForm methods.

Once your form is wrapped with FormContext, useFormContext: Function can be invoked in its child component.

Note: invoke useFormContext will give you all 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"

function YourForm() {
  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} />
}

React Native

You will get the same performance enhancement from Uncontrolled Component. However, there are certain API which isn't compatible with React Native (duo to the API difference from web and native). We will have to use custom register in the following example.

import React from "react"
import { Text, View, TextInput, Button, Alert } from "react-native"
import useForm from 'react-hook-form'

export default function App() {
  const { register, setValue, handleSubmit, errors } = useForm()
  const onSubmit = data => Alert.alert('Form Data', data)

  return (
    <View>
      <Text>First name</Text>
      <TextInput
        ref={register({ name: 'firstName'}, { required: true })}
        onChangeText={text => setValue('firstName', text, true)}
      />
      {errors.firstName && <Text>This is required.</Text>}

      <Text>Last name</Text>
      <TextInput
        ref={register({ name: 'lastName'})}
        onChangeText={text => setValue('lastName', text)}
      />

      <View>
        <Button onPress={handleSubmit(onSubmit)} />
      </View>
    </View>
  )
}

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 YourForm() {
  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>
  )
}

NativeValidation

The following example demonstrates how you can leverage the browser's validation. You only need to set nativeValidation to true and rest of the syntax is the same as standard validation.

CodeSandbox
import React from "react";
import useForm from "react-hook-form";

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

TypeScript

React Hook Form is built with Typescript, so you can define FormData type to support form values.

CodeSandbox
import React from "react"
import useForm from "react-hook-form"

type FormData = {
  firstName: string,
  lastName: string
}

export default function YourForm() {
  const { register, setValue, handleSubmit } = useForm<FormData>()
  const onSubmit = handleSubmit(({ name, email }) => {}) // firstName and lastName will have correct type

  return (
    <form onSubmit={onSubmit}>
      <input name="firstName" ref={register} />
      <input name="lastName" ref={register} />
      <button
        type="button"
        onClick={() => {
          setValue(lastName, "luo") // ✅
          setValue(firstName, true) // ❌: true is not string
          errors.bill // ❌: property bill does not exist
        }}
      >
        SetValue
      </button>
    </form>
  )
}

Need your support

If you find React Hook Form is useful, please star the repo to support 🙏🏻