Skip to content


React hooks for controlled component

(props?: UseControllerProps) => { field: object, fieldState: object, formState: object }

This custom hook powers Controller. Additionally, it shares the same props and methods as Controller. It's useful for creating reusable Controlled input.


The following table contains information about the arguments for useController.

namestringUnique name of your input.
controlObjectcontrol object provided by invoking useForm. Optional when using FormProvider.
defaultValueanyThe same as an uncontrolled component's defaultValue. When passing a boolean value, it will be treated as checkbox input. For more details, see useForm's defaultValues section.
  • You need to either set defaultValue at the field-level or call useForm with defaultValues. If both are set,devaultValues will be used.

    Note: inline defaultValue is required when working with useFieldArray by integrating with the value from fields object.

  • If your form will invoke reset with default values, you will need to call useForm with defaultValues instead of setting the defaultValue on individual fields.

  • Setting defaultValue inline or at useForm can not be undefined.

Validation rules in the same format as for register.

Important: doesn't supportvalueAs for useController.

rules={{ required: true }}
shouldUnregisterboolean = false

Input will be unregistered after unmount and defaultValues will be removed as well.


The following table contains information about properties which useController produces.

Object NameNameTypeDescription
fieldonChange(value: any) => void

A function which sends the input's value to the library. It should be assigned to the onChange prop of the input.

onBlur() => void

A function which sends the input's onBlur event to the library. It should be assigned to the input's onBlur prop.


The current value of the controlled component.


Input's name being registered.


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


Invalid state for current input.


Touched state for current controlled input.


Dirty state for current controlled input.


error for this specific input.


Indicates whether the form was successfully submitted.


Set to true after the user modifies any of the inputs.


Set to true after the form is submitted. Will remain true until the reset method is invoked.


An object with the user-modified fields. Make sure to provide all inputs' defaultValues via useForm, so the library can compare the input value against the defaultValue.


An object containing all the inputs the user has interacted with.


true if the form is currently being submitted. false if otherwise.


Number of times the form was submitted.


Set to true if the form doesn't have any errors.


Set to true during validation.

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

function Input({ control, name }) {
  const {
    field: { ref, ...inputProps },
    fieldState: { invalid, isTouched, isDirty },
    formState: { touchedFields, dirtyFields }
  } = useController({
    rules: { required: true },
    defaultValue: "",

  return <TextField {...inputProps} inputRef={ref} />;

function App() {
  const { control } = useForm();
  return <Input name="firstName" control={control} />;
import * as React from "react";
import { useForm, useController, UseControllerProps } from "react-hook-form";

type FormValues = {
  FirstName: string;

function Input(props: UseControllerProps<FormValues>) {
  const { field, fieldState } = useController(props);

  return (
      <input {...field} placeholder={} />
      <p>{fieldState.isTouched && "Touched"}</p>
      <p>{fieldState.isDirty && "Dirty"}</p>
      <p>{fieldState.invalid ? "invalid" : "valid"}</p>

export default function App() {
  const { handleSubmit, control } = useForm<FormValues>({
    defaultValues: {
      FirstName: ""
    mode: "onChange"
  const onSubmit = (data: FormValues) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Input control={control} name="FirstName" rules={{ required: true }} />
      <input type="submit" />


  • Do not register input again. This custom hook is designed to take care of the registration process.

    const { field } = useController({ name: 'test' })
    <input {...field} /> // ✅ 
    <input {...field} {...register('test')} /> // ❌ double up the registration        
  • It's ideal to use a single useController per component. If you need to use more than one, make sure you rename the prop. May want to consider using Controller instead.

    const { field: input } = useController({ name: 'test' })
    const { field: checkbox } = useController({ name: 'test1' })
    <input {...input} />
    <input {...checkbox} />