Vijit Ail Software Engineer at toothsi. I work with React and NodeJS to build customer-centric products.

The complete guide to React Hook Form

6 min read 1712

React logo.

Forms are an essential part of how users interact with websites and web applications. Validating the user’s data passed through the form is a crucial responsibility for a developer.

React-hook-form is a library that helps you validate forms in React. React-hook-form is a minimal library without any other dependencies. It is performant and straightforward to use, requiring developers to write fewer lines of code than other form libraries.

In this guide, you will learn how to use the React Hook Form library to build excellent forms in React without using any complicated render prop or higher-order components.

React Hook Form: Introduction

React Hook Form takes a slightly different approach than other form libraries in the React ecosystem. React Hook Form adopts the use of uncontrolled inputs using ref instead of depending on the state to control the inputs. This approach makes the forms more performant and reduces the number of re-renders.

The package size is very tiny and minimal: just 9.1KB minified + gzipped, and it has zero dependencies. The API is very intuitive that provides a seamless experience to developers when working with forms. React Hook Form follows HTML standards for validating the forms using constraint-based validation API.

Another great feature offered by React Hook Form is its painless integration with UI libraries, since most libraries support ref.

To install React Hook Form, run the following command:

npm install react-hook-form

Simple register form

In this section, you will learn about the fundamentals of the useForm Hook by creating a very basic registration form.

First, import the useForm Hook from the react-hook-form package.

import { useForm } from "react-hook-form";

Then, inside your component, use the Hook as follows:

We made a custom demo for .
No really. Click here to check it out.

const { register, handleSubmit } = useForm();

The useForm Hook returns an object containing few properties. For now, you only require register and handleSubmit.

The register method helps you register an input field into the React Hook Form so that it is available for the validation and its value can be tracked for changes. To register the input, pass the register method to ref>/code> prop of the input.

<input type="text" ref={register} name="firstName" />

An important point to note here is that the input component must have a name prop, and its value should be unique.

The handleSubmit method, as the name suggests, manages form submission. It needs to be passed as a prop to the onSubmit prop of the form component.

The handleSubmit method can handle two functions as arguments. The first function passed as an argument will be invoked along with the registered field values when the form validation is successful. The second function is called with errors when the validation fails.

const onFormSubmit  = data => console.log(data);

const onErrors = errors => console.error(errors);

<form onSubmit={handleSubmit(onFormSubmit, onErrors)}>
{/* ... */}
</form>

Now that you have a fair idea about the basic usage of the useForm Hook, take a look at a more real-world example:

import React from "react";
import { useForm } from "react-hook-form";
import { Form, FormGroup, Label, Input, Button } from "reactstrap";
const RegisterForm = () => {
  const { register, handleSubmit } = useForm();
  const handleRegistration = (data) => console.log(data);
  return (
    <Form onSubmit={handleSubmit(handleRegistration)}>
      <FormGroup>
        <Label>Name</Label>
        <Input name="name" innerRef={register} />
      </FormGroup>
      <FormGroup>
        <Label>Email</Label>
        <Input type="email" name="email" innerRef={register} />
      </FormGroup>
      <FormGroup>
        <Label>Password</Label>
        <Input type="password" name="password" innerRef={register} />
      </FormGroup>
      <Button color="primary">Submit</Button>
    </Form>
  );
};
export default RegisterForm;

As you can see, no other components were imported to track the input values. The useForm Hook makes the component code cleaner and easier to maintain. And since the form is uncontrolled, you do not have to pass props like onChange and value to each input.

In the example above, the reactstrap library is used for building the form UI. You may notice here that the register method was passed to the innerRef prop instead of ref; this is because the reactstrap form components give access to the native DOM input using the innerRef prop.

You can use any other UI library of your choice for creating the form. Make sure to check the prop for accessing the reference to the native input component.

In the next section, you will learn how to handle form validation in the above form.

Handling Validation and Errors

To apply validations to a field, you can pass validation options to the register method. The validation options are similar to the existing HTML form validation standard.

The validation options include the following properties.

  • required – Indicates if the field is required or not. If this property is set to true then the field cannot be empty.
  • minlength and maxlength sets the minimum and maximum length for a string input value.
  • min and max – Sets the minimum and maximum values for numerical value.
  • type – Indicates the type of the input field; it can be email, number, text, or any other standard HTML input types.
  • pattern – Defines a pattern for the input value using a regular expression.

If you want to mark a field as required, you can write something as follows.

<Input name="name" innerRef={register({ required: true })} />

On submit, this will result in the following error object.

{
name: {
  type: "required"
  message: ""
  ref: <INPUT name="name" type="text" class="form-control"></INPUT>
  }
}

Here, the type property refers to the type of the failed validation, and the ref property contains the native DOM input element.

You can also pass an error message for the field by passing a string instead of a boolean to the validation property.

// ...
<Form onSubmit={handleSubmit(handleRegistration, handleError)}>
  <FormGroup>
      <Label>Name</Label>
      <Input name="name" innerRef={register({ required: "Name is required" })} />
  </FormGroup>
</Form>

You can also access the error object from the useForm Hook.

const { register, handleSubmit, errors } = useForm();

Below you can find the complete example.

import React from "react";
import { useForm } from "react-hook-form";
import { Form, FormGroup, Label, Input, Button } from "reactstrap";
const RegisterForm = () => {
  const { register, handleSubmit, errors } = useForm();
  const handleRegistration = (data) => console.log(data);
  const handleError = (errors) => {};
  const registerOptions = {
    name: { required: "Name is required" },
    email: { required: "Email is required" },
    password: {
      required: "Password is required",
      minLength: {
        value: 8,
        message: "Password must have at least 8 characters"
      }
    }
  };
  return (
    <Form onSubmit={handleSubmit(handleRegistration, handleError)}>
      <FormGroup>
        <Label>Name</Label>
        <Input name="name" innerRef={register(registerOptions.name)} />
        <small className="text-danger">
          {errors.name && errors.name.message}
        </small>
      </FormGroup>
      <FormGroup>
        <Label>Email</Label>
        <Input
          type="email"
          name="email"
          innerRef={register(registerOptions.email)}
        />
        <small className="text-danger">
          {errors.email && errors.email.message}
        </small>
      </FormGroup>
      <FormGroup>
        <Label>Password</Label>
        <Input
          type="password"
          name="password"
          innerRef={register(registerOptions.password)}
        />
        <small className="text-danger">
          {errors.password && errors.password.message}
        </small>
      </FormGroup>
      <Button color="primary">Submit</Button>
    </Form>
  );
};
export default RegisterForm;

If you want to validate the field when there is an onChange or onBlur event, you can pass a mode property to the useForm Hook.

const { register, handleSubmit, errors } = useForm({
  mode: "onBlur"
});

You can find more details on the useForm hook in the API reference.

Usage with third-party components

In some cases, the external UI component you want to use in the form may not support ref and can only be controlled by the state.

React Hook Form has provisions for such use cases, and can easily integrate with any third party controlled component.

React Hook Form provides a wrapper component called Controller that allows you to register a controlled external component, similar to how the register method works. In this case, instead of the register method, you will use the control object from the useForm Hook.

const { register, handleSubmit, errors, control } = useForm();

Consider that you have to create a role field in your form that will accept values from a select input. You can create the select input using the react-select library.

The control object should be passed to the control prop of the Controller component, along with the name of the field. You can specify the validation rules using the rules prop.

The controlled component should be passed to the Controller component using the as prop. The as prop will inject onChange, onBlur, and value prop to the component. The Select component also requires an options prop to render the drop-down options. So, you can add the options prop directly to the Controller component and it will be passed to the Select component, along with any other additional props.

<Controller
  name="role"
  control={control}
  as={Select}
  options={selectOptions}
  defaultValue=""
  rules={registerOptions.role}
/>

You can check out the complete example for the role field below.

import { useForm, Controller } from "react-hook-form";
import Select from "react-select";
// ...
const { register, handleSubmit, errors, control } = useForm({
  mode: "onBlur"
});

const selectOptions = [
  { value: "student", label: "Student" },
  { value: "developer", label: "Developer" },
  { value: "manager", label: "Manager" }
];

const registerOptions = {
  // ...
  role: { required: "Role is required" }
};

// ...

<FormGroup>
  <Label>Your Role</Label>
  <Controller
    name="role"
    control={control}
    as={Select}
    options={selectOptions}
    defaultValue=""
    rules={registerOptions.role}
  />
  <small className="text-danger">
    {errors.role && errors.role.message}
  </small>
</FormGroup>

You can go through the API reference for the Controller component here for a detailed explanation.

Conclusion

React Hook Form is an excellent addition to the React Open Source ecosystem. It has made creating and maintaining forms much easier for developers. The best part about this library is that it focuses more on developer experience, and is very flexible to work with. React Hook Form also integrates well with state management libraries and works excellent in React Native.

That was it from this guide. You can check out the full code and demo for your reference here. Until next time, stay safe and keep building more forms. Cheers ✌

References

React Hook Form documentation

Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — .

Vijit Ail Software Engineer at toothsi. I work with React and NodeJS to build customer-centric products.

2 Replies to “The complete guide to React Hook Form”

  1. this post is helpful thank you for your work. But it needs updating because of the syntax updates in v7 of RHF especially with the controller. have a nice day

Leave a Reply