React forms with react-hook-form, Yup and tailwindcss

React forms with react-hook-form, tailwindcss and Yup for form input validation.

Umesh Chaudhary
7 min readDec 5, 2022

--

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 implement 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:

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.23b

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:

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:

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 UserRegistrationFormtionForm

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.

{
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:

<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>
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:

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:

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:

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.

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:

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

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

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

to

<input type="text" {…register('username')} />

Final UserRegistrationForm components looks like this:

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

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

Happy coding!

--

--