React forms with react-hook-form, tailwindcss and Yup for form input validation.
Tags: react, tailwindcss, yup, reacthookform
Published: 2022-12-05 14:54:19

In this tutorial, we will learn how to create a simple user registration form with React, react-hook-form, tailwindcss, and Yup. We will use the useForm hook from react-hook-form to manage the state of the form and to register and validate input fields. We will imlement Yup for input validation in the form and use the tailwind css to style the input fields.

## Introduction

Forms are a common feature of many web applications, and creating one with the popular React JavaScript library and the `react-hook-form` library is a straightforward process.
In this tutorial, we'll walk through the steps to create a simple user registration form with three input fields: username, email, and password.
We'll use the latest version of `react-hook-form (v7.40.0)` and the `tailwindcss(v3.0.23)` CSS framework to style our form input fields.

## Create project

First, let's create a new React project using the `create-react-app` command-line tool:

```bash
npx create-react-app my-user-registration-form
```

## Installation

Next, we'll need to install the `react-hook-form` and `tailwindcss` packages using npm or yarn:

```
npm install react-hook-form@7.40.0 tailwindcss@3.0.23
```

or

```
yarn add react-hook-form@7.40.0 tailwindcss@3.0.23
```

## Implementation

Once the packages are installed, we can import the `useForm` hook from `react-hook-form` in our `UserRegistrationForm.jsx` file:

```jsx
import { useForm } from 'react-hook-form'
```

We'll use the `useForm` hook to manage the state of our form input fields and to perform input validation.

Next, let's create a UserRegistrationForm component that will render our user registration form.
Inside the UserRegistrationForm component, we'll use the useForm hook to initialize the form state and to register our input fields:

```jsx
const UserRegistrationForm = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm()

  return (
    <div className="m-auto flex w-1/2 flex-col gap-4">
      <h1 className="bold text-2xl underline">Registration Form</h1>
      <form onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-2">
        <div className="input-wrapper flex flex-col">
          <label htmlFor="username">Username</label>
          <input
            type="text"
            {...register('username', {
              required: 'Username is required',
              minLength: {
                value: 3,
                message: 'Username must be at least 3 characters',
              },
            })}
          />
          {errors.username && (
            <p className="text-xs italic text-red-500">{errors.username.message}</p>
          )}
        </div>

        <div className="input-wrapper flex flex-col">
          <label htmlFor="email">Email</label>
          <input
            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 && <p className="text-xs italic text-red-500">{errors.email.message}</p>}
        </div>

        <div className="input-wrapper flex flex-col">
          <label htmlFor="password">Password</label>
          <input
            type="password"
            {...register('password', {
              required: 'Password is required',
              minLength: {
                value: 8,
                message: 'Password must be at least 8 characters',
              },
            })}
          />
          {errors.password && (
            <p className="text-xs italic text-red-500">{errors.password.message}</p>
          )}
        </div>
      </form>
    </div>
  )
}

export default UserRegistrationForm
```

We're using the register method provided by the `useForm` hook to register each input field.
We're also passing in validation rules for each field, which will be enforced when the form is submitted.

If the user enters an invalid value for any of the input fields, the errors object will contain an error message for that field, which we're rendering below each input field.

```jsx
{
  errors.password && <p className="text-xs italic text-red-500">{errors.password.message}</p>
}
```

Finally, let's add a submit button to our form and handle the form submission in an onSubmit function:

```jsx
<div className="input-wrapper">
  <button
    type="submit"
    className="focus:shadow-outline rounded bg-blue-500 py-2 px-4 font-bold text-white hover:bg-blue-700 focus:outline-none"
  >
    Submit
  </button>
</div>
```

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

The onSubmit function will be called when the user submits the form, and it will receive the form data as an argument.
In this example, we're simply logging the form data to the console, but in a real application, you would likely use this data to create a new user in your database or send it to your server for further processing.

Here's the complete `UserRegistrationForm` component:

```jsx
import React from 'react'
import { useForm } from 'react-hook-form'

const UserRegistrationForm = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm()

  // Form submission handler
  const onSubmit = (data) => {
    // Do something with the form data
  }

  return (
    <div className="m-auto flex w-1/2 flex-col gap-4">
      <h1 className="bold text-2xl underline">Registration Form</h1>
      <form onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-2">
        <div className="input-wrapper flex flex-col">
          <label htmlFor="username">Username</label>
          <input
            type="text"
            {...register('username', {
              required: 'Username is required',
              minLength: {
                value: 3,
                message: 'Username must be at least 3 characters',
              },
            })}
          />
          {errors.username && (
            <p className="text-xs italic text-red-500">{errors.username.message}</p>
          )}
        </div>

        <div className="input-wrapper flex flex-col">
          <label htmlFor="email">Email</label>
          <input
            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 && <p className="text-xs italic text-red-500">{errors.email.message}</p>}
        </div>

        <div className="input-wrapper flex flex-col">
          <label htmlFor="password">Password</label>
          <input
            type="password"
            {...register('password', {
              required: 'Password is required',
              minLength: {
                value: 8,
                message: 'Password must be at least 8 characters',
              },
            })}
          />
          {errors.password && (
            <p className="text-xs italic text-red-500">{errors.password.message}</p>
          )}
        </div>

        <div className="input-wrapper">
          <button
            type="submit"
            className="focus:shadow-outline rounded bg-blue-500 py-2 px-4 font-bold text-white hover:bg-blue-700 focus:outline-none"
          >
            Submit
          </button>
        </div>
      </form>
    </div>
  )
}

export default UserRegistrationForm
```

To use the `UserRegistrationForm` component in our `App` component, we can simply import it and render it in the `App` component's render method:

```jsx
import UserRegistrationForm from './UserRegistrationForm'

function App() {
  return (
    <div className="App">
      <UserRegistrationForm />
    </div>
  )
}
```

And that's it! We now have a fully-functioning user registration form with three input fields and input validation using the `react-hook-form` and `tailwindcss` libraries.
You can further customize the form and its input fields by using the various styling options provided by `tailwindcss`.

## Yup implementation

Another option for input validation in a React form is to use the `Yup` package.
`Yup` is a JavaScript schema builder for value parsing and validation.
It can be used with `react-hook-form` to provide a simple and straightforward way to validate input fields in a form.

To use `Yup` with `react-hook-form`, you will first need to install the Yup package:

```
npm install yup
npm install @hookform/resolvers@2.9.10
```

or with yarn

```
yarn add yup
yarn add @hookform/resolvers@2.9.10
```

Once the package is installed, you can import the `object` method from Yup in your `UserRegistrationForm` component:

```jsx
import { object, string } from 'yup'
import { yupResolver } from '@hookform/resolvers/yup'
```

Next, we'll create a Yup validation schema for our user registration form.
The schema will define the rules for each input field and the error messages that should be displayed if the user enters an invalid value for that field:
Yupresolver is used to tell the form to use yup schema instead of the builtin methods for validation.

```jsx
const validationSchema = object().shape({
  username: string()
    .required('Username is required')
    .min(3, 'Username must be at least 3 characters'),
  email: string().required('Email is required').email('Invalid email address'),
  password: string()
    .required('Password is required')
    .min(8, 'Password must be at least 8 characters'),
})
```

We can now use the `useForm` hook to initialize the form state and to register our input fields, passing in the validation schema as an option in resolver:

```jsx
const {
  register,
  handleSubmit,
  formState: { errors },
} = useForm({
  resolver: yupResolver(validationSchema),
})
```

Now we can just simply remove the inbuilt validation from the input field.

```jsx
<input
  type="text"
  {...register('username', {
    required: 'Username is required',
    minLength: {
      value: 3,
      message: 'Username must be at least 3 characters',
    },
  })}
/>
```

to

```jsx
<input type="text" {...register('username')} />
```

Final `UserRegistrationForm` components looks like this:

```jsx
import React from 'react'
import { useForm } from 'react-hook-form'
import { object, string } from 'yup'
import { yupResolver } from '@hookform/resolvers/yup'

const UserRegistrationForm = () => {
  const validationSchema = object().shape({
    username: string()
      .required('Username is required')
      .min(3, 'Username must be at least 3 characters'),
    email: string().required('Email is required').email('Invalid email address'),
    password: string()
      .required('Password is required')
      .min(8, 'Password must be at least 8 characters'),
  })

  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    resolver: yupResolver(validationSchema),
  })

  // Form submission handler
  const onSubmit = (data) => {
    // Do something with the form data
  }

  return (
    <div className="m-auto flex w-1/2 flex-col gap-4">
      <h1 className="bold text-2xl underline">Registration Form</h1>
      <form onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-2">
        <div className="input-wrapper flex flex-col">
          <label htmlFor="username">Username</label>
          <input type="text" {...register('username')} />
          {errors.username && (
            <p className="text-xs italic text-red-500">{errors.username.message}</p>
          )}
        </div>

        <div className="input-wrapper flex flex-col">
          <label htmlFor="email">Email</label>
          <input type="email" {...register('email')} />
          {errors.email && <p className="text-xs italic text-red-500">{errors.email.message}</p>}
        </div>

        <div className="input-wrapper flex flex-col">
          <label htmlFor="password">Password</label>
          <input type="password" {...register('password')} />
          {errors.password && (
            <p className="text-xs italic text-red-500">{errors.password.message}</p>
          )}
        </div>

        <div className="input-wrapper">
          <button
            type="submit"
            className="focus:shadow-outline rounded bg-blue-500 py-2 px-4 font-bold text-white hover:bg-blue-700 focus:outline-none"
          >
            Submit
          </button>
        </div>
      </form>
    </div>
  )
}

export default UserRegistrationForm
```

Using Yup for input validation in a React form is a simple and elegant way to enforce validation rules and display error messages.
It provides a declarative and intuitive syntax for defining validation rules, and it integrates seamlessly with react-hook-form.

## Screenshots

![Example screenshot 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/luzi6s1q3fkkjnd9pz4t.png)

![Example screenshot 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3mss45znlqsbjx2enlu0.png)



I hope this tutorial has been helpful in showing you how to create a user registration form with `react-hook-form`, `tailwindcss` and `yup`.
Here is the link for the following code : [Gist to UserRegistrationForm](https://gist.github.com/umschaudhary/de933a20a1a152fa9aac6ae08044588b)

Happy coding!