Skip to content

Controller

Integrate controlled external UI inputs with React Hook Form.

</> Controller: ControllerProps

React Hook Form embraces uncontrolled components and native inputs; however, it's hard to avoid working with external controlled components such as React-Select, AntD and MUI. This wrapper component makes it easier for you to work with them.

Note: if you simply want to control a field's value from outside the form, it's not necessary to use Controller. You can simply use the values option of useForm.

Props


The following table contains information about the arguments for Controller.

NameTypeRequiredDescription
nameFieldPathUnique name of your input.
controlControlThe control object is from invoking useForm. Optional when using FormProvider.
renderFunctionThis is a render prop. A function that returns a React element and provides the ability to attach events and value into the component. This simplifies integrating with external controlled components with non-standard prop names. Provides field (with onChange, onBlur, name, ref, value), fieldState, and formState objects to the render callback.
rulesObjectValidation rules in the same format for register options, which includes:

required, min, max, minLength, maxLength, pattern, validate
shouldUnregisterboolean = falseInput will be unregistered after unmount and defaultValues will be removed as well.

Note: this prop should be avoided when using with useFieldArray as unregister function gets called after input unmount/remount and reorder.
disabledboolean = falseSince v7.46.0 disabled prop will be returned from field prop. Controlled input will be disabled and its value will be omitted from the submission data.
defaultValueunknownImportant: Cannot apply undefined to defaultValue or defaultValues at useForm.
  • You need to either set defaultValue at the field level or useForm's defaultValues. If you used defaultValues at useForm, skip using this prop.
  • If your form will invoke reset with default values, you will need to provide useForm with defaultValues.
  • Calling onChange with undefined is not valid. You should use null or the empty string as your default/cleared value instead.
  • If field.value is undefined on first render, the input starts out uncontrolled and React will warn when it later becomes controlled — always set defaultValue/defaultValues to avoid this.
exactboolean = trueSince v7.68.0 This prop will enable an exact match for input name subscriptions, default to true.

Return


The following table contains information about properties which Controller produces.

Object NameNameTypeDescription
fieldonChange(value: any) => voidA function which sends the input's value to the library.

It should be assigned to the onChange prop of the input and the value should not be undefined.
This prop updates formState and you should avoid manually invoking setValue or other APIs related to field updates.
fieldonBlur() => voidA function which sends the input's onBlur event to the library. It should be assigned to the input's onBlur prop.
fieldvalueunknownThe current value of the controlled component.
fielddisabledbooleanThe disabled state of the input.
fieldnamestringInput's name being registered.
fieldrefReact.RefA ref used to connect hook form to the input. Assign ref to component's input ref to allow hook form to focus the error input — this only works if the target component forwards refs (React.forwardRef) or exposes an equivalent, like MUI's inputRef.
fieldStateinvalidbooleanInvalid state for current input.
fieldStateisTouchedbooleanTouched state for current controlled input.
fieldStateisDirtybooleanDirty state for current controlled input.
fieldStateerrorobjectSince v7.0.0 error for this specific input.
formStateisDirtybooleanSet to true after the user modifies any of the inputs.
  1. Important: Make sure to provide all inputs' defaultValues at the useForm level, so the library can have a single source of truth to compare whether the form is dirty.
  2. File-type inputs will need to be managed at the app level due to the ability to cancel file selection and FileList object.
formStatedirtyFieldsobjectAn object with the user-modified fields. Make sure to provide all inputs' defaultValues via useForm, so the library can compare against the defaultValues
  1. Important: Make sure to provide defaultValues at the useForm level, so the library can have a single source of truth to compare each field's dirtiness.
  2. Dirty fields will not represent as isDirty formState, because dirty fields are marked field dirty at the field level rather than the entire form. If you want to determine the entire form state use isDirty instead.
formStatetouchedFieldsobjectAn object containing all the inputs the user has interacted with.
formStatedefaultValuesobjectSince v7.37.0 The value that was set in useForm's defaultValues or updated defaultValues via the reset API.
formStateisSubmittedbooleanSet to true after the form has been submitted. Will remain true until the reset method is invoked.
formStateisSubmitSuccessfulbooleanIndicates that the form was successfully submitted without any runtime error.
formStateisSubmittingbooleantrue if the form is currently being submitted. false otherwise.
formStateisLoadingbooleanSince v7.41.0 true if the form is currently loading async default values.
Important: this prop is only applicable to async defaultValues
formStatesubmitCountnumberThe number of times the form was submitted.
formStateisValidbooleanSet to true if the form doesn't have any errors.

setError immediately forces isValid to false; this value is not itself derived from validation and will be overwritten the next time validation runs (e.g. on the next onChange, submit, or trigger() call).
formStateisValidatingbooleanSet to true during validation.
formStatevalidatingFieldsobjectSince v7.51.0 Captures fields that are undergoing asynchronous validation.
formStateerrorsobjectAn object with field errors. There is also an ErrorMessage component to retrieve error messages easily.
formStatedisabledbooleanSince v7.48.0 Set to true if the form is disabled via the disabled prop in useForm.
formStateisReadybooleanSince v7.56.0 Set to true when formState subscription setup is ready.
Examples:

Web

import ReactDatePicker from "react-datepicker"
import { TextField } from "@mui/material"
import { useForm, Controller } from "react-hook-form"
type FormValues = {
ReactDatepicker: string
}
function App() {
const { handleSubmit, control } = useForm<FormValues>()
return (
<form onSubmit={handleSubmit((data) => console.log(data))}>
<Controller
control={control}
name="ReactDatepicker"
render={({ field: { onChange, onBlur, value, ref } }) => (
<ReactDatePicker
onChange={onChange} // send the value to hook form
onBlur={onBlur} // notify when the input is touched or blurred
selected={value}
/>
)}
/>
<input type="submit" />
</form>
)
}
import ReactDatePicker from "react-datepicker"
import { TextField } from "@mui/material"
import { useForm, Controller } from "react-hook-form"
function App() {
const { handleSubmit, control } = useForm()
return (
<form onSubmit={handleSubmit((data) => console.log(data))}>
<Controller
control={control}
name="ReactDatepicker"
render={({ field: { onChange, onBlur, value, ref } }) => (
<ReactDatePicker
onChange={onChange}
onBlur={onBlur}
selected={value}
/>
)}
/>
<input type="submit" />
</form>
)
}

React Native

import { Text, View, TextInput, Button, Alert } from "react-native"
import { useForm, Controller } from "react-hook-form"
export default function App() {
const {
control,
handleSubmit,
formState: { errors },
} = useForm({
defaultValues: {
firstName: "",
lastName: "",
},
})
const onSubmit = (data) => console.log(data)
return (
<View>
<Controller
control={control}
rules={{
required: true,
}}
render={({ field: { onChange, onBlur, value } }) => (
<TextInput
placeholder="First name"
onBlur={onBlur}
onChangeText={onChange}
value={value}
/>
)}
name="firstName"
/>
{errors.firstName && <Text>This is required.</Text>}
<Controller
control={control}
rules={{
maxLength: 100,
}}
render={({ field: { onChange, onBlur, value } }) => (
<TextInput
placeholder="Last name"
onBlur={onBlur}
onChangeText={onChange}
value={value}
/>
)}
name="lastName"
/>
<Button title="Submit" onPress={handleSubmit(onSubmit)} />
</View>
)
}

Video


The following video showcases what's inside Controller and how it was built.

TIP
  • It's important to be aware of each prop's responsibility when working with external controlled components such as MUI, AntD, and Chakra UI. Controller acts as a "spy" on your input by reporting and setting its value.

    • onChange: send data back to hook form
    • onBlur: report that the input has been interacted with (focus and blur)
    • value: set up the input's initial and updated value
    • ref: allow the input to be focused when there is an error
    • name: give the input a unique name The following CodeSandboxes demonstrate the usage:
    • MUI and other components
    • Chakra UI components
  • Do not register input again. This component is made to take care of the registration process.

    <Controller
    name="test"
    render={({ field }) => {
    // return <input {...field} {...register('test')} />; ❌ double up the registration
    return <input {...field} /> // ✅
    }}
    />
  • Customise what value gets sent to hook form by transforming the value during onChange.

    <Controller
    name="test"
    render={({ field }) => {
    // sending integer instead of string.
    return (
    <input
    {...field}
    onChange={(e) => field.onChange(parseInt(e.target.value))}
    />
    )
    }}
    />

Thank you for your support

If you find React Hook Form to be useful in your project, please consider starring and supporting it.

Edit