React Hook Form’s Controller: When and Why to Use It

Frank LamFrank Lam
5 min read

When building forms in React, managing form state and validations can get tricky, especially when using third-party form components like Material-UI or Ant Design, which don’t integrate seamlessly with React Hook Form’s native inputs. This is where Controller comes into play—it provides a way to wrap external form components and still leverage React Hook Form’s powerful features like validation, state management, and error handling.

In this tutorial, we’ll explore what the Controller component does, why you need it, and how to use it effectively.


What is Controller in React Hook Form?

Controller is a component provided by React Hook Form that acts as a bridge between third-party form inputs (like Material-UI, Ant Design, or custom components) and React Hook Form's state management.

React Hook Form manages form inputs by default using its register method. However, when you are working with controlled components—components where state is managed externally (e.g., Material-UI’s <TextField />), using register won’t work as expected. The Controller component gives you control over those external inputs, allowing you to still use the same validation and state management features provided by React Hook Form.


When to Use Controller

You should use Controller when:

  1. Working with Controlled Components: Many third-party UI libraries use controlled components (where the component’s state is controlled by React), and React Hook Form’s register function only works out of the box with uncontrolled components. Controller bridges this gap.

  2. Complex Form Components: If a form component has additional behavior or logic, such as a date picker or a custom select box, you’ll need Controller to properly integrate it into the form.

  3. Needing Dynamic Value Management: If your form input changes based on other fields or external factors, Controller gives you full control over the value and how it's updated.


Why Use Controller?

  • Easy Integration with UI Libraries: Many UI libraries like Material-UI, Ant Design, or React Date Picker use controlled components. Controller allows you to integrate them into your form seamlessly.

  • Centralized Validation: You can manage all your validation logic inside Controller, keeping your form logic consistent across controlled and uncontrolled components.

  • Full Control: You can control the input's value, handle onChange events, and use React Hook Form's methods like validation and error handling, even with third-party components.


How to Use Controller

Let’s go through a step-by-step tutorial on how to use Controller with Material-UI's <TextField /> component as an example.

1. Setting Up React Hook Form

First, install React Hook Form and Material-UI if you haven’t already.

npm install react-hook-form @mui/material @emotion/react @emotion/styled

Now import the necessary hooks and components into your project:

import React from 'react';
import { useForm, Controller } from 'react-hook-form';
import { TextField, Button } from '@mui/material';

2. Creating a Simple Form

Create a basic form using React Hook Form’s useForm hook and the Controller component to wrap Material-UI’s <TextField />.

const MyForm = () => {
  const { handleSubmit, control, formState: { errors } } = useForm();

  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {/* First Name Field with Controller */}
      <Controller
        name="firstName"
        control={control}
        defaultValue=""
        rules={{ required: 'First name is required' }}
        render={({ field }) => (
          <TextField
            {...field}
            label="First Name"
            variant="outlined"
            error={!!errors.firstName}
            helperText={errors.firstName ? errors.firstName.message : ''}
          />
        )}
      />

      {/* Submit Button */}
      <Button type="submit" variant="contained" color="primary">
        Submit
      </Button>
    </form>
  );
};

3. Key Points Explained

  • name: The name prop specifies the field name (in this case, "firstName") which is used by React Hook Form to track its value and validation state.

  • control: The control prop is passed from the useForm hook and is required by Controller to manage the form state.

  • defaultValue: This sets the initial value of the input field. For controlled components, you must provide a defaultValue.

  • rules: Validation rules are defined here. In this example, the "firstName" field is required.

  • render: This prop is where you specify how the input should be rendered. Inside the render prop, we pass all the necessary field props ({...field}) to the Material-UI <TextField />, making it work seamlessly with React Hook Form.

4. Handling Validation Errors

React Hook Form provides a simple way to handle validation errors. In the above example, if the "firstName" field is empty when submitting the form, an error will be shown below the input using helperText.

The error state is managed by errors.firstName:

error={!!errors.firstName}
helperText={errors.firstName ? errors.firstName.message : ''}

This allows you to display the validation message right next to the input field, enhancing user experience.


Advanced Example with Multiple Controllers

Here’s an example where we integrate both a text input and a select dropdown using Controller.

import { MenuItem, Select } from '@mui/material';

const MyAdvancedForm = () => {
  const { handleSubmit, control } = useForm();

  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {/* Text Input Field */}
      <Controller
        name="username"
        control={control}
        defaultValue=""
        render={({ field }) => <TextField {...field} label="Username" />}
      />

      {/* Select Dropdown */}
      <Controller
        name="role"
        control={control}
        defaultValue=""
        render={({ field }) => (
          <Select {...field} label="Role">
            <MenuItem value="admin">Admin</MenuItem>
            <MenuItem value="user">User</MenuItem>
          </Select>
        )}
      />

      <Button type="submit">Submit</Button>
    </form>
  );
};

In this example, both the TextField and Select components are wrapped in Controller, ensuring their state is properly managed by React Hook Form.


Why You Should Use Controller

  1. Flexibility: With Controller, you gain complete control over the form inputs, allowing you to use third-party or custom components.

  2. Validation: You can define custom validation rules within the Controller, enabling consistent validation logic across your forms.

  3. Error Management: Easily manage error states and provide helpful feedback to users with error messages displayed alongside the inputs.

  4. Simplicity: It simplifies the integration of complex components (like date pickers or sliders) while maintaining the power of React Hook Form’s validation and state management.


Conclusion

The Controller component in React Hook Form is a crucial tool when working with controlled components or third-party UI libraries like Material-UI or Ant Design. It provides you with full control over form inputs, allowing for flexible and powerful form handling while maintaining a clean, declarative syntax.

0
Subscribe to my newsletter

Read articles from Frank Lam directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Frank Lam
Frank Lam