How to use React Hook Form to add Form Validation in React
React Hook Form to add Form Validation

How to use React Hook Form to add Form Validation in React

Dead Simple Chat Team

Table of Contents

Dead Simple Chat allows you to add chat in minutes to your React Application with the powerful JavaScript Chat SDK and API.

Doing client-side form validation is essential in almost every web application. Form validation prevents the user from submitting invalid data to the server, and also entails presenting users with helpful error messages.

In this blog post, we will see how to use the library "React Hook Form" to easily add form validation in React applications.

What is React Hook Form?

React Hook Form is a React library that allows you to add form validation using the useForm custom Hook provided by the library.

It is an actively developed library and has more than 34K stars on Github and is used by 344k repositories and has over 258 active contributors.

It is very small, around 25 kb minified and only 9.1 kb minified + gzipped.

It supports React UI libraries and can be used with Schema validators like Joi, Yup, Zod etc.

How to use React Hook Form?

Let's look at a basic example using React Hook Form to build a simple signup form.

You first need to install the react hook form package, to install the package run the command:

npm install react-hook-form

After the package is installed, let's import it into our application

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

Then, we will create a SignupForm component, the SignupForm component will call the useForm method and the method returns some more useful methods that we can use to add validation to our React applications.

Let's create our SignupForm component to hold our Signup form:

function SignupForm() {
    const { register, 
           handleSubmit, 
           watch, 
           formState: { errors } 
          } = useForm();
    
    //..
}
  • register method is used to register the form elements with the react-use-form to allow state management and validation
  • handleSubmit is used to handle the form submission. You can call handleSubmit and pass it a method that you would like to be called when the form is submitted. handleSubmit will check if the form has any errors, if it has no errors then only it will call your onSubmit method.
  • watch is a special method that is used to watch and input. You can pass it the name of the input, and you can retrieve the value of the input each time the input changes.
  • error object contains details about the error when the form validation fails.

Let's continue building our component using react-use-form library:

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

function SignupForm() {
  const {
    register,
    handleSubmit,
    watch,
    formState: { errors }
  } = useForm();

  const formSubmit = (data) => {
    console.log("Form Submitted: ", data);
  };

  return (
    <form onSubmit={handleSubmit(formSubmit)}>
      <div>
        <label htmlFor="email">Email:</label>
        <input
          id="email"
          type="email"
          {...register("email", {
            required: "Email is required",
            pattern: {
              value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i,
              message: "Invalid email address"
            }
          })}
        />
        {errors.email && <span>{errors.email.message}</span>}
      </div>

      <div>
        <label htmlFor="password">Password:</label>
        <input
          id="password"
          type="password"
          {...register("password", {
            required: "Password is required",
            minLength: {
              value: 8,
              message: "Password must be at least 8 characters"
            }
          })}
        />
        {errors.password && <span>{errors.password.message}</span>}
      </div>

      <div>
        <label htmlFor="confirmPassword">Confirm Password:</label>
        <input
          id="confirmPassword"
          type="password"
          {...register("confirmPassword", {
            required: "Please confirm your password",
            validate: (value) =>
              value === watch("password") || "Passwords do not match"
          })}
        />
        {errors.confirmPassword && (
          <span>{errors.confirmPassword.message}</span>
        )}
      </div>

      <button type="submit">Register</button>
    </form>
  );
}

export default function App() {
  return (
    <div>
      <SignupForm />
    </div>
  );
}

In the above code example, we have created a form element, and passing the handleSubmit method to the onSubmit prop and passing our own formSubmit method to the handleSubmit.

When the form is submitted, the handleSubmit method from "react-use-form" will be called.

The handleSubmit method will check if the form has any validation errors, if there are no validation errors then handleSubmit will call the formSubmit method.

Let's look at our email field to understand how we are using the "react-use-form" to validate the email field:

      <div>
        <label htmlFor="email">Email:</label>
        <input
          id="email"
          type="email"
          {...register("email", {
            required: "Email is required",
            pattern: {
              // Regex to validate email
              value: /^[\w-]+(\.[\w-]+)*@[A-Za-z0-9-]+(\.[A-Za-z0-9]+)*(\.[A-Za-z]{2,})$/i,
              message: "Invalid email address"
            }
          })}
        />
        {errors.email && <span>{errors.email.message}</span>}
      </div>

We are creating an input element of type email to accept the email from the user, then we are calling the register(<field_name>, { <options> }) method to register the input field with the "react-use-form" library.

The register method takes the name of the field as a first argument, the name is used to fetch the errors for that field, retrieve its value or watch for changes. The <field_name> is the required parameter.

Then under options we pass an object, with key required, you can pass required a boolean of true or the message that you want to display to the user if the field is empty. Here we are setting the message "Email is required".

Next is the pattern object, it takes two keys value and message, the value field accepts a regex that you can specify to validate the field and a message to display if the validation fails.

Any validation errors will be saved in the errors object, to check if a field as errors, we use errors.<field_name_specified_in_register> .

In our case it is errors.email, if it is not null then we are displaying the error message:

{errors.email && <span>{errors.email.message}</span>}

Here is the codesanbox to try out the validation example using react-hook-form

Deeper Look at the register method

The register method allows you to register the field with react-hook-form, lets's explore the most common options available that are available in the register method.

The register method allows to set the name of the input tag, when the form is submitted the name of the input tag is added as a key to the object and value is set to the value of the input tag.

For e.g if we look at our example, when the SignupForm is submitted, the formSubmit method was called with the data variable as an argument to the formSubmit method.

The data variable would contain the following data:

Now if we want to tweak how the value is saved in the submit result, then we can change the name specified in the register method:

Index Input Name Submit Result (Data)
1 register("firstName") {firstName: 'value'}
2 register("name.firstName") {name: { firstName: 'value' }}
3 register("name.firstName.0") {name: { firstName: [ 'value' ] }}

If we look at the example at index 1 it is the same as our registration form example where we are specifying the name of the input and it is added as a key to the submitted result.

The second example, "name.firstName" create as an object inside our data object called as name and then set the value of the firstName as name.firstName.

Javascript Chat API and SDK | DeadSimpleChat
Easily add chat to your website or app within seconds. Pre-built chat with API and Javascript SDK | DeadSimpleChat.

Common options available in the register method

Now let's look at commonly used options available in the register method:

required (boolean/number)

You can set the required value to true to make the field required or you can add a return to return an error message:

Example 1:

<input {...register("firstName", { required: true }) } />

Example 2:

<input {...register("firstName", { required: "Please enter first name" }) } />

maxLength and minLength (number)

The maxLength and minLength properties are used the specify the maximum and minimum length of the input.

The fields can be used separately or together, let's see some examples:

Example 1:

<input {...register("password", { minLength: 8 }) } />

Example 2:

<input {...register("tweet", { maxLength: 140 }) } />

Example 3:

<input {...registerText("description", { minLength: 10, maxLength: 100 }) } />

pattern (regex)

The pattern attribute accepts a regex pattern and validates the input, you can also pass and pattern an object, with keys value and message. The value field will contain the regex and the message field will contain a message to display when pattern doesn't match the input.

Let see some examples:

Example 1:

<input {...registerText("domainName", { pattern: /^([a-z\d]+(-[a-z\d]+)*\.)+[a-z]{2,}$/i }) } />

In the above example, we called the registerText method and gave it a pattern to validate the domain name.

Example 2:

<input {...registerText("domainName", { pattern: { value: /^([a-z\d]+(-[a-z\d]+)*\.)+[a-z]{2,}$/i, message: "Invalid Domain Name" }) } />

In the second example, we specified the pattern and a message when the pattern does not match.

validate (method)

The validate props take a method as an argument. You can use it to run custom validation.

Any method that you provide as value to the validate option will be called when the form is submitted.

The method will be called with two arguments, the first argument will contain the current value of the input, the second argument will contain formValues object with the all other values in the form.

Consider the example below:

<input {...register("zone", { validate: (value, formValues) => { return zones.indexOf(value) > -1 } }) } />

The value argument will contain the current value of the input and the formValues will contain the value of the complete form.

Javascript Chat API and SDK | DeadSimpleChat
Easily add chat to your website or app within seconds. Pre-built chat with API and Javascript SDK | DeadSimpleChat.

Integrating with Existing UI Libraries

Let's look at an example on how to integrate the React Hook Form with existing UI libraries.

Typically, the UI libraries give you components that you can use in your application.

So, for input tag, checkbox etc. there will be custom components that you add to your application and it will display and nicely formatted component on the screen.

But the React Hook Form uses ref to register the components and it is not guaranteed that the UI library would expose the ref.

Therefore when using React Hook Form you can use the Controller component to register the custom UI component with React Hook form.

Here is an example:

import "./styles.css";
import "react-datepicker/dist/react-datepicker.css";

import ReactDatePicker from "react-datepicker";
import { useForm, Controller } from "react-hook-form";

export default function App() {
  const { handleSubmit, control } = useForm();

  function onSubmit(value) {
    console.log(value);
  }

  return (
    <div className="App">
      <form onSubmit={handleSubmit(onSubmit)}>
        <Controller
          name="dateOfBirth"
          control={control}
          rules={{ required: true }}
          render={({ field }) => (
            <ReactDatePicker selected={field.value} onChange={field.onChange} />
          )}
        />
        <input type="submit" />
      </form>
    </div>
  );
}

In the above code, we are using the "react-datepicker" library with React Hook Form.

To use the library with custom components we have to use <Controller /> component by "react-hook-form" and pass it attributes: name control rules and render.

The name attribute specifies the name of the field, it is similar to the name value that we pass to the render method.

The control attribute requires the control that we get from the useForm() method.

rules are similar to the rules that we specify as an option to the register method, here we add rules like validate, required, minLength, maxLength etc.

Then the render prop we pass a method that returns the component from our UI library and using the field object we can bind the attributes of the custom component.

Check out the example in Code Sandbox:

Dead Simple Chat allows you to add chat in minutes to your React Application with the powerful JavaScript Chat SDK and API.

You might be interested in some of our other articles

Conclusion

In the blog post, we have learned how to use react-hook-form to easily add form validation to your react applications.

We have also seen how you can use react hook form with custom UI libraries, and add validation to the form field offered by the Custom UI libraries views as well.