# useController

> React hook for building reusable controlled inputs with full access to field value, validation state, and form state from React Hook Form.

Source: https://react-hook-form.com/docs/usecontroller

## `useController:` ([UseControllerProps](https://react-hook-form.com/ts#UseControllerProps)) => [UseControllerReturn](https://react-hook-form.com/ts#UseControllerReturn)

This custom hook powers [`Controller`](https://react-hook-form.com/docs/usecontroller/controller). Additionally, it shares the same props and methods as `Controller`. It's useful for creating reusable Controlled input.

### Props

---

The following table contains information about the arguments for `useController`.

| Name               | Type                                            | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| ------------------ | ----------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `name`             | [FieldPath](https://react-hook-form.com/ts#FieldPath) | ✓        | Unique name of your input. Reactive — the controller re-subscribes when this prop changes, allowing dynamic field name switching.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `control`          | [Control](https://react-hook-form.com/ts#Control)     |          | [`control`](https://react-hook-form.com/docs/useform/control) object provided by invoking `useForm`. Optional when using `FormProvider`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `rules`            | `Object`                     |          | Validation rules in the same format for `register`, which includes: `required`, `min`, `max`, `minLength`, `maxLength`, `pattern`, `validate`.<br/><br/>`rules={{ required: true }}`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `shouldUnregister` | `boolean = false`            |          | Input 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.                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `disabled`         | `boolean = false`            |          | _(since 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.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `defaultValue`     | `unknown`                    |          | **Important:** Cannot apply `undefined` to `defaultValue` or `defaultValues` at `useForm`.<ul><li>You need to either set `defaultValue` at the field-level or `useForm`'s `defaultValues`. `undefined` is not a valid value. If you used `defaultValues` at `useForm`, skip using this prop.</li><li>If your form will invoke `reset` with default values, you will need to provide `useForm` with `defaultValues`.</li><li>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.</li></ul>                                       |
| `exact`            | `boolean = true`             |          | _(since v7.68.0)_ This prop will enable an exact match for input name subscriptions, default to true. _(since v7.81.0)_ With `exact: true`, the field is also updated when one of its ancestor paths is set (for example, a field named `"users.0.name"` reflects `setValue("users.0", { name: "Jane" })`), but not when a nested child path of its value changes (for example, `setValue("users.0.name", "Jane")` does not update a field named `"users.0"`). Pass `exact: false` to also receive nested updates. **Note:** this differs from [`useWatch`](https://react-hook-form.com/docs/usewatch) and [`useFormState`](https://react-hook-form.com/docs/useformstate), which both default `exact` to `false`. |

### Return

---

The following table contains information about properties which `useController` produces.

| Object Name  | Name                 | Type                                      | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| ------------ | -------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `field`      | `onChange`           | `(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 and value should **not be `undefined`**. This prop updates [formState](https://react-hook-form.com/docs/useform/formstate) and you should avoid manually invoking [`setValue`](https://react-hook-form.com/docs/useform/setvalue) or other API related to field update. It accepts either a change event or a value: given an event, it reads `target.checked` for checkboxes, `target.files` for file inputs and `target.value` otherwise. Any object with a `target` property is treated as an event. |
| `field`      | `onBlur`             | `() => void`           | A function which sends the input's onBlur event to the library. It should be assigned to the input's `onBlur` prop.                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `field`      | `value`              | `unknown`              | The current value of the controlled component.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `field`      | `disabled`           | `boolean`              | The disabled state of the input.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `field`      | `name`               | `string`               | Input's name being registered.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `field`      | `ref`                | `React.Ref`            | 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.                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `fieldState` | `invalid`            | `boolean`              | Invalid state for current input.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `fieldState` | `isTouched`          | `boolean`              | Touched state for current controlled input.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `fieldState` | `isDirty`            | `boolean`              | Dirty state for current controlled input.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `fieldState` | `isValidating`       | `boolean`              | Validating state for current controlled input.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `fieldState` | `error`              | `object`               | _(since v7.0.0)_ error for this specific input.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `formState`  | `isDirty`            | `boolean`              | Set to `true` after the user modifies any of the inputs. **Important:** Make sure to provide all inputs' `defaultValues` at `useForm`, so hook form can have a single source of truth to compare whether the form is dirty.                                                                                                                                                                                                                                                                                                                                          |
| `formState`  | `dirtyFields`        | `object`               | An object with the user-modified fields. Make sure to provide all inputs' `defaultValues` via `useForm`, so the library can compare against the `defaultValues`.                                                                                                                                                                                                                                                                                                                                                                                                     |
| `formState`  | `touchedFields`      | `object`               | An object containing all the inputs the user has interacted with.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `formState`  | `defaultValues`      | `object`               | _(since v7.37.0)_ The value that was set in [`useForm`](https://react-hook-form.com/docs/useform)'s `defaultValues` or updated `defaultValues` via [`reset`](https://react-hook-form.com/docs/useform/reset) API.                                                                                                                                                                                                                                                                                                                                                                                                 |
| `formState`  | `isSubmitted`        | `boolean`              | Set to `true` after the form is submitted. Will remain `true` until the `reset` method is invoked.                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `formState`  | `isSubmitSuccessful` | `boolean`              | Indicates that the form was successfully submitted without any runtime error.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `formState`  | `isSubmitting`       | `boolean`              | `true` if the form is currently being submitted. `false` otherwise.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `formState`  | `isLoading`          | `boolean`              | _(since v7.41.0)_ `true` if the form is currently loading async default values. **Important:** this prop is only applicable to async `defaultValues`.                                                                                                                                                                                                                                                                                                                                                                                                       |
| `formState`  | `submitCount`        | `number`               | The number of times the form was submitted.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `formState`  | `isValid`            | `boolean`              | Set 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).                                                                                                                                                                                                                                                                                            |
| `formState`  | `isValidating`       | `boolean`              | Set to `true` during validation.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `formState`  | `validatingFields`   | `object`               | _(since v7.51.0)_ Captures fields that are undergoing asynchronous validation.                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `formState`  | `errors`             | `object`               | An object with field errors. There is also an [ErrorMessage](https://react-hook-form.com/docs/useformstate/errormessage) component to retrieve error messages easily.                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `formState`  | `disabled`           | `boolean`              | _(since v7.48.0)_ Set to `true` if the form is disabled via the `disabled` prop in [`useForm`](https://react-hook-form.com/docs/useform#disabled).                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `formState`  | `isReady`            | `boolean`              | _(since v7.56.0)_ Set to true when `formState` subscription setup is ready.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |

### Examples

---

<div>

**JS**

[Open in CodeSandbox](https://codesandbox.io/s/usecontroller-i0ywh)

```javascript
import { TextField } from "@mui/material"
import { useController, useForm } from "react-hook-form"

function Input({ control, name, label }) {
  const { field, fieldState } = useController({
    name,
    control,
    rules: { required: `${label} is required` },
  })

  return (
    <TextField
      label={label}
      onChange={field.onChange} // send the value to hook form
      onBlur={field.onBlur} // notify when the input is touched or blurred
      value={field.value} // input value
      name={field.name} // send the input name
      inputRef={field.ref} // send input ref, so we can focus on the input when an error appears
      error={fieldState.invalid}
      helperText={fieldState.error?.message}
    />
  )
}

export default function App() {
  const { handleSubmit, control } = useForm({
    defaultValues: { firstName: "" },
  })

  return (
    <form onSubmit={handleSubmit((data) => console.log(data))}>
      <Input control={control} name="firstName" label="First name" />
      <button type="submit">Submit</button>
    </form>
  )
}
```

**TS**

[Open in CodeSandbox](https://codesandbox.io/s/usecontroller-0o8px)

```tsx
import { useForm, useController, UseControllerProps } from "react-hook-form"

type FormValues = {
  FirstName: string
}

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

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

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" />
    </form>
  )
}
```

</div>

[Open in CodeSandbox](https://codesandbox.io/s/usecontroller-checkboxes-99ld4)

```javascript
import { useState } from "react"
import { useController, useForm } from "react-hook-form"

const Checkboxes = ({ options, control, name }) => {
  const { field } = useController({
    control,
    name,
  })
  const [value, setValue] = useState(field.value || [])

  return (
    <>
      {options.map((option, index) => (
        <input
          onChange={(e) => {
            const valueCopy = [...value]

            // update checkbox value
            valueCopy[index] = e.target.checked ? e.target.value : null

            // send data to react hook form
            field.onChange(valueCopy)

            // update local state
            setValue(valueCopy)
          }}
          key={option}
          checked={value.includes(option)}
          type="checkbox"
          value={option}
        />
      ))}
    </>
  )
}

export default function App() {
  const { register, handleSubmit, control } = useForm({
    defaultValues: {
      controlled: [],
      uncontrolled: [],
    },
  })
  const onSubmit = (data) => console.log(data)

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <section>
        <h2>uncontrolled</h2>
        <input {...register("uncontrolled")} type="checkbox" value="A" />
        <input {...register("uncontrolled")} type="checkbox" value="B" />
        <input {...register("uncontrolled")} type="checkbox" value="C" />
      </section>

      <section>
        <h2>controlled</h2>
        <Checkboxes
          options={["a", "b", "c"]}
          control={control}
          name="controlled"
        />
      </section>
      <input type="submit" />
    </form>
  )
}
```

<div>

A generic component built on `useController` works with any form, and TypeScript still checks that `name` is a field of the form that `control` belongs to.

```tsx
import {
  useForm,
  useController,
  UseControllerProps,
  FieldValues,
} from "react-hook-form"

function TextInput<T extends FieldValues>(props: UseControllerProps<T>) {
  const { field, fieldState } = useController(props)

  return (
    <div>
      <input {...field} value={field.value ?? ""} />
      {fieldState.error && <p>{fieldState.error.message}</p>}
    </div>
  )
}

type FormValues = { firstName: string; lastName: string }

export default function App() {
  const { control, handleSubmit } = useForm<FormValues>({
    defaultValues: { firstName: "", lastName: "" },
  })

  return (
    <form onSubmit={handleSubmit((data) => console.log(data))}>
      <TextInput
        control={control}
        name="firstName"
        rules={{ required: "Required" }}
      />
      <TextInput control={control} name="lastName" />
      <input type="submit" />
    </form>
  )
}
```

</div>

### Tips

---

- It's important to be aware of each prop's responsibility when working with external controlled components, such as MUI, AntD, Chakra UI. Its job is to spy on the input, report, and set its value.
  - **onChange**: send data back to hook form
  - **onBlur**: report that the input has been interacted with (focus and blur)
  - **value**: set up input initial and updated value
  - **ref**: allow input to be focused with error — this only works if the target component forwards refs (`React.forwardRef`) or exposes an equivalent, like MUI's `inputRef`. If it doesn't accept a ref at all, omit `ref` from what you spread and handle focus manually.
  - **name**: give input an unique name

  It's fine to host your state and combined with `useController`.

[Open in CodeSandbox](https://codesandbox.io/s/usecontroller-own-state-wncet2?file=/src/App.tsx)

  ```javascript
  const { field } = useController({ name: 'test' });
  const [value, setValue] = useState(field.value);

  onChange={(event) => {
    // data sent back to hook form; empty input becomes null instead of NaN
    field.onChange(event.target.value === "" ? null : event.target.valueAsNumber)
    setValue(event.target.value) // UI state
  }}
  ```

- **Focusing components that don't forward a DOM ref:** `field.ref` accepts any object with a `focus` method (and optionally `select`), so you can point it at the component's own focus API:

  ```javascript
  <DatePicker
    {...field}
    ref={(instance) =>
      field.ref(instance && { focus: () => instance.focusInput() })
    }
  />
  ```

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

  ```javascript
  const { field } = useController({ name: 'test' })

  <input {...field} /> // ✅
  <input {...field} {...register('test')} /> // ❌ double up the registration
  ```

- It's best to use a single `useController` call per component — each call creates its own subscription, so multiple calls in one component can cause extra re-renders. If you need more than one controlled field in the same component, rename the destructured `field` for each call to avoid naming collisions, or consider using `Controller` instead.

  ```javascript
  const { field: input } = useController({ name: 'test' })
  const { field: checkbox } = useController({ name: 'test1' })

  <input {...input} />
  <input {...checkbox} />
  ```

### Troubleshooting

---

#### `A component is changing an uncontrolled input to be controlled`

**Why it happens:**

The field has no default value, so `field.value` is `undefined` on the first render. When the user types, the value becomes a string, and React warns that the input changed from uncontrolled to controlled.

**Fix:**

Give the field a default value in `useForm`, even if it is just `""`:

```javascript
useForm({ defaultValues: { firstName: "" } })
```

You can also pass `defaultValue` to `useController`.

#### Changing `defaultValue` does not update the input

**Why it happens:**

The `defaultValue` option of `useController` is only read when the field first registers. Changing it later does not update the field.

**Fix:**

Use `reset` or `setValue` to change the value after the first render, or pass data that loads later to [`values`](https://react-hook-form.com/docs/useform#values) on `useForm`.

#### `Cannot read properties of null (reading '_names')`

**Why it happens:**

`useController` could not find a form: no `control` was passed and the component is not inside a [`FormProvider`](https://react-hook-form.com/docs/formprovider).

**Fix:**

Pass `control` from `useForm`, or wrap the tree in `<FormProvider {...methods}>`. See [`control`](https://react-hook-form.com/docs/useform/control#Cannotreadpropertiesofnullreadingnames).
