# control

> The control object returned by useForm, required to register controlled inputs via the Controller component and useController hook.

Source: https://react-hook-form.com/docs/useform/control

## `control:` [Control](https://react-hook-form.com/ts#Control)

This object contains methods for registering components into React Hook Form. Pass it to [`Controller`](https://react-hook-form.com/docs/usecontroller/controller), [`useController`](https://react-hook-form.com/docs/usecontroller), [`useWatch`](https://react-hook-form.com/docs/usewatch), [`useFieldArray`](https://react-hook-form.com/docs/usefieldarray), [`useFormState`](https://react-hook-form.com/docs/useformstate), or [`FormProvider`](https://react-hook-form.com/docs/formprovider) so those APIs can connect to the same form instance — it's optional wherever the component is already inside a `FormProvider`.

> **Important: Rules**
>
> **Important:** do not access any of the properties inside this object directly; it is for internal use only.
>

### Examples

---

A complete form where a reusable input component receives `control` as a prop. The parent owns the form; the child only connects to it.

**TS**

```tsx
import {
  useForm,
  useController,
  type Control,
  type FieldPath,
  type FieldValues,
} from "react-hook-form"

type FormValues = {
  firstName: string
  email: string
}

type TextInputProps<T extends FieldValues> = {
  control: Control<T>
  name: FieldPath<T>
  label: string
}

function TextInput<T extends FieldValues>({
  control,
  name,
  label,
}: TextInputProps<T>) {
  const { field, fieldState } = useController({
    control,
    name,
    rules: { required: `${label} is required` },
  })

  return (
    <label>
      {label}
      <input {...field} aria-invalid={fieldState.invalid} />
      {fieldState.error && <p role="alert">{fieldState.error.message}</p>}
    </label>
  )
}

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

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <TextInput control={control} name="firstName" label="First name" />
      <TextInput control={control} name="email" label="Email" />
      <button type="submit">Submit</button>
    </form>
  )
}
```

**JS**

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

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

  return (
    <label>
      {label}
      <input {...field} aria-invalid={fieldState.invalid} />
      {fieldState.error && <p role="alert">{fieldState.error.message}</p>}
    </label>
  )
}

export default function App() {
  const { control, handleSubmit } = useForm({
    defaultValues: { firstName: "", email: "" },
  })
  const onSubmit = (data) => console.log(data)

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <TextInput control={control} name="firstName" label="First name" />
      <TextInput control={control} name="email" label="Email" />
      <button type="submit">Submit</button>
    </form>
  )
}
```

### Tips

---

- Think of `control` as an internal object that you only pass along. Give it to the APIs listed above, and do not read its properties or call its methods yourself. They are not part of the public API and can change in any release.
- Inside a [`FormProvider`](https://react-hook-form.com/docs/formprovider), you usually do not need `control` at all. Hooks such as [`useController`](https://react-hook-form.com/docs/usecontroller), [`useWatch`](https://react-hook-form.com/docs/usewatch), and [`useFormState`](https://react-hook-form.com/docs/useformstate) read it from context.
- If you pass `control` as a prop, type it as `Control<FormValues>`, or make the component generic to reuse it across forms. See [the TypeScript error below](#TypeControlFormValuesisnotassignabletotypeControlFieldValues) if the types don't line up.

### Troubleshooting

---

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

**Why it happens:**

`useController` or `Controller` could not find a form. The component is rendered outside a [`FormProvider`](https://react-hook-form.com/docs/formprovider) and no `control` prop was passed. Hooks such as `useWatch` fail the same way, with a different property name in the message.

**Fix:**

Pass `control` from `useForm`, or wrap the tree in `<FormProvider {...methods}>`.

#### A field is missing from the submitted data

**Why it happens:**

The field is connected to a different form than the one that submits. Usually `useForm` was called twice — once in the parent and again in a child — and the field received the child's `control` while the parent's `handleSubmit` runs.

**Fix:**

Call `useForm` once and pass that `control` down, or use `FormProvider`.

#### `Type 'Control<FormValues>' is not assignable to type 'Control<FieldValues>'`

**Why it happens:**

`Control` is tied to one exact form shape. A prop typed as `Control<FieldValues>`, or as a smaller shape such as `Control<{ email: string }>`, does not accept the `control` of a larger form.

**Fix:**

For a component used by one form, type the prop as `Control<FormValues>`. For a component reused across forms, make it generic, as `TextInput` does in the example above. `name` is then checked against the form that owns `control`:

```tsx
function TextInput<T extends FieldValues>(props: {
  control: Control<T>
  name: FieldPath<T>
}) {
  // ...
}
```
