Bulletproof Forms In React With Yup
Published: 2024-04-19 05:58:39

by [Christabel Ogochukwu](https://blog.openreplay.com/authors/christabel-ogochukwu)





<blockquote><em>
Have you ever created a form in React and tried to extract the data from it, but it seems frustrating? Or have you ever wanted to create a sophisticated form with just a little code from your end? If so, you are in luck; this article introduces a powerful solution that leverages the strengths of [`Yup`](https://www.npmjs.com/package/yup), a popular validation library, and [`React-hook-form`](https://react-hook-form.com/), streamlining the form development process and empowering you to build bulletproof forms quickly.
</em></blockquote>




<div style="background-color:#efefef; border-radius:8px; padding:10px; display:block;">
<hr/>
<h3><em>Session Replay for Developers</em></h3> 
<p><em>Uncover frustrations, understand bugs and fix slowdowns like never before with <strong><a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a></strong> — an open-source session replay suite for developers. It can be <strong>self-hosted</strong> in minutes, giving you complete control over your customer data.</em></p>
<img alt="OpenReplay" style="margin-top:5px; margin-bottom:5px;" width="768" height="400" src="https://raw.githubusercontent.com/openreplay/openreplay/main/static/openreplay-git-hero.svg" class="astro-UXNKDZ4E" loading="lazy" decoding="async">
<p><em>Happy debugging! <a href="https://openreplay.com" target="_blank">Try using OpenReplay today.</a></em><p>
<hr/>
</div>




What shall we do here? We'll delve into the intricacies of using `Yup` for robust validation, exploring data types, custom validation schemes, and error handling. Furthermore, we'll shed light on `React-hook-form`, a powerful tool that seamlessly integrates `Yup` validation with React components, enabling efficient form handling and state management.

`Yup` is a JavaScript library designed specifically for schema-based validation. It allows developers to define rules and constraints that user input must adhere to. These rules are encapsulated in a [schema](https://schema.org/docs/documents.html) and written concisely. `Yup` offers a rich set of built-in validation methods, such as:
* Required fields: Enforce that a field must not be empty.
* Type checking: Ensure fields contain specific data types like numbers, strings, or emails.
* Minimum and maximum lengths: Define a field's allowed character or value range.
* Custom validation logic: Create custom validation rules tailored to specific requirements.

While `Yup` excels in schema-based validation, integrating it directly into React applications can involve boilerplate code. The `React-hook-form`  library simplifies this process. It bridges `Yup` and React, enabling developers to integrate `Yup` schemas into their forms.

## Setting Up Your React Project
Now that we know what `Yup` and `React-hook-form` are, let us learn how to set them up in a React project.

We will use [Vite](https://vitejs.dev/) to create our React project for this article. If you do not have Vite installed on your system and will want to do so to follow up this tutorial, you can easily do that by going to your command prompt or terminal, navigating to where you would like to create the React project, and run this command:
```shell
npm create vite@latest
```
When the command runs, you can easily navigate through the rest, which will be the project's name, whether a JavaScript or Typescript project, etc.

## Installing Yup and React Hook Form
Now that we have our React project up and running, let us explore how to add `Yup` to your React project.

The next thing to do, and I am sure you can guess it already, is to install `Yup` using either `npm` or `yarn`. 
```javascript
npm install yup 
```
I am using `npm` for this article, but you can use `yarn` if that is what you usually use. 

Now that we have `Yup` installed, we will need to install a `Yup` resolver using this command:
```javascript
npm @hookform/resolvers yup
```
## Creating a Simple React Form
Since we have everything installed, let us create a simple form in a component to which we will link our `Yup` and `React-hook-form`. Here is the code for the form:
```javascript
const YupForm = () => {
  return (
    <form>
      <div>
        <label htmlFor="">Username</label>
        <input type="text" />
      </div>
      <div>
        <label htmlFor="">Email</label>
        <input type="text" />
      </div>
      <div>
        <label htmlFor="">Password</label>
        <input type="password" />
      </div>
      <button>Submit</button>
    </form>
  );
};
export default YupForm;
```
The code above shows a form containing three user inputs: Username, Email, and Password. When we run the code, this should be our output:
![Screenshot 2024-03-26 154139](https://blog.openreplay.com/images/bulletproof-forms-in-react-with-yup/images/image1.png)

At this point, the form will not be able to validate a user's input, and that is precisely where `Yup` comes in. `Yup` is going to validate the form based on a schema that we are going to define.

## Defining a Yup Validation Schema
Before we define our form's schema, we need to understand what a schema is. A schema is a formal definition of the rules and constraints that user input must adhere to. It is a blueprint for validating user-submitted data in your React forms.

Now, let us define the schema for our form. To do so, we must first run the command:
```javascript
import * as yup from "yup";
```
This command imports everything regarding `Yup` into our component, and this is what we want. 

Now, let's set up our form schema. To create our schema, we have to define our object using the `object()` function:
```javascript
const userSchema = yup.object();
```
Now that we have defined our object, we will give the object a shape. We will specify what each property of the form should contain. It specifies the expected structure of the data. To do this, we will use the `shape({})` function:
```javascript
const userSchema = yup.object().shape({
  username: yup.string().required(),
  email: yup.string().email().required(),
  password: yup.string().min(4).max(10).required(),
});
```
From the code above, the schema has three properties: `username`, `email`, and `password`, which correspond with the input of our form. Here is a breakdown of what each property is validating based on:
* `username: yup.string().required()`: Requires a non-empty string value for the username property, meaning that `username` input cannot be empty on submission. If it is, then `Yup` will reject the submission.
* `email: yup.string().email().required()`: Requires a non-empty string value that matches a valid email format for the `email` property.
* `password: yup.string().min(4).max(10).required()`: Requires a non-empty string value for the password property. This property enforces a minimum length of 4 characters and a maximum length of 10 characters for the password.

## Integrating Yup with React Hook Form
Now that we have our schema set, we need to connect the `React-hook-form` library to our schema, `userSchema`, which validates our form. Before doing that, we must create a form using `React-hook-form`. To achieve this, we will import the `useForm` function from `React-hook-form`. Here is the code snippet:
```javascript
import { useForm } from "react-hook-form";
```
The code above will give us access to the already-made functionalities in the `React-hook-form` library, such as error handling, validation, form submission, registration, etc.

The next step is to import `yupResolver` from `@hookform/resolvers/yup` to the package. This package will bridge the `Yup` validation schemas and the `React-hook-form` `useForm` hook. It translates the validation rules in your `Yup` schema into a `React-hook-form` format for form state management and error handling. Here is a code snippet on how to import `yupResolver` into our component:
```javascript
import { yupResolver } from "@hookform/resolvers/yup";
```
Now, we have our `yupResolver` imported into our component; we can connect `Yup` and `React-hook-form` to the fields of our form.


<CTA_Middle_Basics />



## Connecting Yup to Our Form Fields
To connect `Yup` to our form to use its features, we need to state which features we need and connect them to our `useForm` while using `yupResolver`. Here is a code snippet on how it is done:
```javascript
const {
  register,
  handleSubmit,
  formState: { errors },
} = useForm({
  resolver: yupResolver(userSchema),
});
```
This code snippet above utilizes the `useForm` hook from the `React-hook-form` to manage a form with validation powered by `Yup`. Let's break it down step by step:
* `register`: feature registers form fields with `React-hook-form`. You'll pass it the name of the field and any additional options for configuration.
* `handleSubmit`: feature handles form submissions. You can pass it a callback function that will be executed when the form is submitted successfully.
* `formState: { errors }`: This nested destructuring extracts the `errors` property from the `formState` object within the returned object from `useForm`. The `errors` property is an object that holds validation errors associated with each registered form field.
* `useForm` is a function imported from `React-hook-form`. It's a powerful hook that helps manage form states, handle submissions, and integrate validation libraries like `Yup`.
* `resolver: yupResolver(userSchema)`: passed to `useForm` configures the validation mechanism.
* `yupResolver`: imported from `@hookform/resolvers yup` acts as an integration layer between `Yup` and `React-hook-form`.
* `userSchema`: This is where the schema we created earlier in this tutorial is stored.

Now, we will need to `register` each field in our form while referencing the name of that field in our schema. For example, our `username` input tag should look like this when the `register` has been added to it:
```html
<input type="text" name="username" {...register("username")} />;
```
When we add a `register` to all the fields of our form, this is what the form should look like:
```html
<form>
  <div>
    <label htmlFor="">Username</label>
    <input type="text" name="username" {...register("username")} />
  </div>
  <div>
    <label htmlFor="">Email</label>
    <input type="text" name="email" {...register("email")} />
  </div>
  <div>
    <label htmlFor="">Password</label>
    <input type="password" name="password" {...register("password")} />
  </div>
  <button>Submit</button>
</form>
```
By adding `...register` to each form field, like in the code snippet above, `Yup` will be able to listen and validate our form, but we also want to submit our forms. That is where `handleSubmit` comes in.

## Handling Form Submission
To submit the data from our form, we must define a function to handle the successful form submission and then pass `handleSubmit()` to our form's onSubmit handler. This is what I mean; let us create a function that handles the form submission; the function's name is `handleFormSubmission`. Now let's have a look at what it contains:
```javascript
const handleFormSubmission = async (data) => {};
```
The code above is a function that sends the data from our form to the server. Let us pass this function into our form's `handleSubmit()` function. To do that, add the code below to the `<form>` tag:
```html
<form onSubmit={handleSubmit(handleFormSubmission)}>
```
The code above connects our `handleFormSubmission` function to yup's `handleSubmit`.

Let's submit our form and see what happens. To ensure our form is submitted, I will create a state that renders the value Successfully submitted when submitted. Here is the code:
```javascript
import { useForm } from "react-hook-form";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useState } from "react";

const App = () => {
  const [submitted, setSubmitted] = useState(false);
  const userSchema = yup.object().shape({
    username: yup.string().required(),
    email: yup.string().email().required(),
    password: yup.string().min(4).max(10).required(),
  });

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

  const handleFormSubmission = async (data) => {
    setSubmitted(true);
  };
  return (
    <>
      <form onSubmit={handleSubmit(handleFormSubmission)}>
        <div>
          <label htmlFor="">Username</label>
          <input type="text" name="username" {...register("username")} />
          <p>{errors.username?.message}</p>
        </div>
        <div>
          <label htmlFor="">Email</label>
          <input type="text" name="email" {...register("email")} />
          <p>{errors.email?.message}</p>
        </div>
        <div>
          <label htmlFor="">Password</label>
          <input type="password" name="password" {...register("password")} />
          <p>{errors.password?.message}</p>
        </div>
        <button>Submit</button>
      </form>
      {submitted ? "Successfully submitted" : null}
    </>
  );
};
export default App;
```
When we submit our form, this should be our output:
![VID-20240326-WA0090_1](https://blog.openreplay.com/images/bulletproof-forms-in-react-with-yup/images/image2.gif)
Our form was submitted successfully, as all the requirements from our schema were met.

## Displaying Validation Errors
To improve our work's user experience, we want to display validation errors whenever they occur. To display validation errors, we will need to create a placeholder for the error of each field as they might be unique and also get the errors from `formState: { errors }`.

Using ternary operators, we can extract the error from `formState: { errors }`. Here is a snippet:
```javascript
<p>{errors.username?.message}</p>
```
The code snippet above means the error message should render if there is an error in the username field. This is how we will place it in our form:
```javascript
<form onSubmit={handleSubmit(handleFormSubmission)}>
  <div>
    <label htmlFor="">Username</label>
    <input type="text" name="username" {...register("username")} />
    <p>{errors.username?.message}</p>
  </div>
  <div>
    <label htmlFor="">Email</label>
    <input type="text" name="email" {...register("email")} />
    <p>{errors.email?.message}</p>
  </div>
  <div>
    <label htmlFor="">Password</label>
    <input type="password" name="password" {...register("password")} />
    <p>{errors.password?.message}</p>
  </div>
  <button>Submit</button>
</form>
```
Let's test it by trying to submit it without filling in any data:
![VID-20240326-WA0091_1](https://blog.openreplay.com/images/bulletproof-forms-in-react-with-yup/images/image3.gif)
When we try to submit, the various errors relating to each field are rendered.

## Values and Controls Provided By Yup
`Yup` provides various features, values, and controls to facilitate validation. Here are some:
* Schema Definition: `Yup` allows you to define a schema that describes the shape and constraints of the data you expect to receive. You can define schemas for objects, arrays, strings, numbers, dates, and more.
* Validation Rules: You can specify validation rules for each field in your schema. `Yup` supports various validation rules, such as required fields, minimum and maximum values, string length limits, regular expressions, custom validation functions, and more.
* Nested Schemas: `Yup` allows you to define nested schemas for complex data structures. This is useful when you have nested objects or arrays that need to be validated.
* Error Messages: `Yup` allows you to customize error messages for each validation rule. You can provide custom error messages or use default messages from `Yup`.

## Conclusion
This article discussed `Yup` and `React-hook-form`, a powerful combination for building robust and user-friendly forms in React. We also looked into how forms can be submitted and how errors can be rendered, all using `Yup`. With the help of this tutorial, I hope you will be willing to try `Yup` on your next project.