DEV Community

Alaribe Bright
Alaribe Bright

Posted on

Form Validation a breeze with Yup and React Hook form

Hello everyone, today I'll be guiding us on how to add form validation to our React application using Yup and react hook form.

Video version
Video version youtube

Final Demo
Form validation in react

Getting started, I've generated a new react project and setup tailwind css.
Incase you want to add tailwind css to your react project please follow this guide tailwindcss-react

This is what I currently have:
App.js

function App() {
  return <div className="w-screen h-screen bg-gradient-to-r from-blue-900 to-purple-900 grid place-content-center">

  </div>;
}

export default App;

Enter fullscreen mode Exit fullscreen mode

Next let's create a components folder that will hold our Form component

src/components/Form/Form.jsx

At this point we're going to create our form component and have our various form inputs

const textInputClassName =
  "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500";

const Form = () => {
  return (
    <div className="md:w-[500px] shadow-sm shadow-white bg-white w-[320px] mx-auto px-7 py-4 rounded-xl">
      <form className="w-full">
        <div className="mb-6">
          <label
            htmlFor="email"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
          >
            Your email
          </label>
          <input
            type="email"
            id="email"
            className={textInputClassName}
            placeholder="test@test.com"
          />
        </div>
        <div className="mb-6">
          <label
            htmlFor="password"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
          >
            Your password
          </label>
          <input type="password" id="password" className={textInputClassName} />
        </div>
        <div className="mb-6">
          <label
            htmlFor="confirmPassword"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
          >
            Confirm Password
          </label>
          <input
            type="password"
            id="confirmPassword"
            className={textInputClassName}
          />
        </div>
        <div className="mb-6">
          <label
            htmlFor="accountType"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-400"
          >
            Select an option
          </label>
          <select
            id="accountType"
            className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
          >
            <option value="">Account Type</option>
            <option value="personal">Personal</option>
            <option value="commercial">Commercial</option>
          </select>
        </div>
        <div className="flex justify-between mb-6">
          <div className="flex">
            <div className="flex items-center h-5">
              <input
                id="remember"
                type="checkbox"
                value=""
                className="w-4 h-4 bg-gray-50 rounded border border-gray-300 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800"
              />
            </div>
            <label
              htmlFor="remember"
              className="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300"
            >
              Remember me
            </label>
          </div>

          <div>
            <label
              htmlFor="default-toggle"
              className="inline-flex relative items-center cursor-pointer"
            >
              <input
                type="checkbox"
                value=""
                id="default-toggle"
                className="sr-only peer"
              />
              <div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
              <span className="ml-3 text-sm font-medium text-gray-900 dark:text-gray-300">
                Toggle me
              </span>
            </label>
          </div>
        </div>

        <button
          type="submit"
          className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
        >
          Submit
        </button>
      </form>
    </div>
  );
};

export default Form;

Enter fullscreen mode Exit fullscreen mode

Now we're done with our Form component JSX, let's go on and add our form to App.js

import Form from "./components/Form/Form";

function App() {
  return (
    <div className="w-screen h-screen bg-gradient-to-r from-blue-900 to-purple-900 grid place-content-center">
      <Form />
    </div>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

Our App.js now looks like this which gives us this result

css-form


Now we've our form design, let's proceed to adding validation. We need to install the following packages

npm install -D yup @hookform/resolvers react-hook-form
or incase you use yarn
yarn add -D yup @hookform/resolvers react-hook-form

Yup is going to be our schema builder for value parsing and validation,
React-hook-form is going to help us validate our form input,
@hookform/resolvers is used to integrate yup and react-hook-form nicely.

Let's import the packages we just installed in our Form component

/components/Form/Form.jsx

import { useForm } from "react-hook-form";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
Enter fullscreen mode Exit fullscreen mode

Before we start building our ValidationSchema, we need to add a name attribute to our html form, as this is important for yup and react-hook-form to keep track of our different inputs.

   <form className="w-full">
        <div className="mb-6">
          <label
            htmlFor="email"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
          >
            Your email
          </label>
          <input
            type="email"
            name="email"
            id="email"
            className={textInputClassName}
            placeholder="test@test.com"
          />
        </div>
        <div className="mb-6">
          <label
            htmlFor="password"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
          >
            Your password
          </label>
          <input type="password" id="password" className={textInputClassName} />
        </div>
        <div className="mb-6">
          <label
            htmlFor="confirmPassword"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
          >
            Confirm Password
          </label>
          <input
            name="password"
            type="password"
            id="confirmPassword"
            className={textInputClassName}
          />
        </div>
        <div className="mb-6">
          <label
            htmlFor="accountType"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-400"
          >
            Select an option
          </label>
          <select
            name="accountType"
            id="accountType"
            className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
          >
            <option value="">Account Type</option>
            <option value="personal">Personal</option>
            <option value="commercial">Commercial</option>
          </select>
        </div>
        <div className="flex justify-between mb-6">
          <div className="flex">
            <div className="flex items-center h-5">
              <input
                id="remember"
                name="remember"
                type="checkbox"
                value=""
                className="w-4 h-4 bg-gray-50 rounded border border-gray-300 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800"
              />
            </div>
            <label
              htmlFor="remember"
              className="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300"
            >
              Remember me
            </label>
          </div>

          <div>
            <label
              htmlFor="toggle"
              className="inline-flex relative items-center cursor-pointer"
            >
              <input
                type="checkbox"
                name="toggle"
                value=""
                id="toggle"
                className="sr-only peer"
              />
              <div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
              <span className="ml-3 text-sm font-medium text-gray-900 dark:text-gray-300">
                Accept
              </span>
            </label>
          </div>
        </div>

        <button
          type="submit"
          className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
        >
          Submit
        </button>
      </form>
Enter fullscreen mode Exit fullscreen mode

Let's build our validation schema, for this I'll create a new schema folder and inside a formSchema.js file.

let's write our formSchema, like this

import * as yup from "yup";

export const registerSchema = yup.object().shape({
  email: yup
    .string("email should be a string")
    .email("please provide a valid email address")
    .required("email address is required"),
});
Enter fullscreen mode Exit fullscreen mode

The email key should match the name attribute in your jsx.

In our Form.js
import { registerSchema } from "../../schema/formSchema";

// Saving space

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

// Saving space
}
Enter fullscreen mode Exit fullscreen mode

register will be used to register our inputs with react-hook-form,
handleSubmit should be added to our form onSubmit and when we submit our form, will help use validate our form,
formState helps us keep track of our form state, in this case error state.

let's add this to our email input, please take note of {...register("email")} and the error jsx

<label
            htmlFor="email"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
          >
            Your email
          </label>
          <input
            {...register("email")}
            type="email"
            name="email"
            id="email"
            className={textInputClassName}
            placeholder="test@test.com"
          />
          {errors.email ? (
            <span className="text-red-900">{errors.email.message}</span>
          ) : (
            <></>
          )}
Enter fullscreen mode Exit fullscreen mode

on our form submit handler, let's add this

<form onSubmit={handleSubmit(formSubmitHandler)} className="w-full">
// saving space
</from
Enter fullscreen mode Exit fullscreen mode

You'll notice we've passed formSubmitHandler which is our custom function that will be passed the form data automatically if validation passes

  const formSubmitHandler = (data) => {
    console.log(data);
  };
Enter fullscreen mode Exit fullscreen mode

With this our form validation is working already and we should have a result like this
Email validation

validating password and confirm password

Let's add the following to our schema file

export const registerSchema = yup.object().shape({
  email: yup
    .string("email should be a string")
    .email("please provide a valid email address")
    .required("email address is required"),
  password: yup
    .string("password should be a string")
    .min(5, "password should have a minimum length of 5")
    .max(12, "password should have a maximum length of 12")
    .required("password is required"),
  confirmPassword: yup
    .string("password should be a string")
    .oneOf([yup.ref("password")])
    .required("confirm password is required"),
});

Enter fullscreen mode Exit fullscreen mode

Back to our Form.js, let's update our password and confirm password to this

<div className="mb-6">
          <label
            htmlFor="password"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
          >
            Your password
          </label>
          <input
            {...register("password")}
            type="password"
            name="password"
            id="password"
            className={textInputClassName}
          />
          {errors.password ? (
            <span className="text-red-900">{errors.password.message}</span>
          ) : (
            <></>
          )}
        </div>
        <div className="mb-6">
          <label
            htmlFor="confirmPassword"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
          >
            Confirm Password
          </label>
          <input
            {...register("confirmPassword")}
            name="confirmPassword"
            type="password"
            id="confirmPassword"
            className={textInputClassName}
          />
          {errors.confirmPassword ? (
            <span className="text-red-900">{errors.confirmPassword.message}</span>
          ) : (
            <></>
          )}
        </div>
Enter fullscreen mode Exit fullscreen mode

This gives us this result

Password and confirm password

Validating Select

Let's update our schema file to the following

import * as yup from "yup";

export const registerSchema = yup.object().shape({
  email: yup
    .string("email should be a string")
    .email("please provide a valid email address")
    .required("email address is required"),
  password: yup
    .string("password should be a string")
    .min(5, "password should have a minimum length of 5")
    .max(12, "password should have a maximum length of 12")
    .required("password is required"),
  confirmPassword: yup
    .string("password should be a string")
    .oneOf([yup.ref("password")])
    .required("confirm password is required"),
  accountType: yup
    .string("account type should be a string")
    .oneOf(["personal", "commercial"])
    .required("account type is required"),
});

Enter fullscreen mode Exit fullscreen mode

Now let's also update our select jsx

<div className="mb-6">
          <label
            htmlFor="accountType"
            className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-400"
          >
            Select an option
          </label>
          <select
            {...register("accountType")}
            name="accountType"
            id="accountType"
            className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
          >
            <option value="">Account Type</option>
            <option value="personal">Personal</option>
            <option value="commercial">Commercial</option>
          </select>{" "}
          {errors.accountType ? (
            <span className="text-red-900">{errors.accountType.message}</span>
          ) : (
            <></>
          )}
        </div>
Enter fullscreen mode Exit fullscreen mode

Now we've this

Select validation

Lastly, let's validate our toggle and checkbox

We start by updating our schema file

import * as yup from "yup";

export const registerSchema = yup.object().shape({
  email: yup
    .string("email should be a string")
    .email("please provide a valid email address")
    .required("email address is required"),
  password: yup
    .string("password should be a string")
    .min(5, "password should have a minimum length of 5")
    .max(12, "password should have a maximum length of 12")
    .required("password is required"),
  confirmPassword: yup
    .string("password should be a string")
    .oneOf([yup.ref("password")])
    .required("confirm password is required"),
  accountType: yup
    .string("account type should be a string")
    .oneOf(["personal", "commercial"])
    .required("account type is required"),
  remember: yup.boolean().oneOf([true], "Please tick checkbox"),
  toggle: yup.boolean().oneOf([true], "Please toggle accept"),
});

Enter fullscreen mode Exit fullscreen mode

Then let's update our from checkbox and toggle jsx

<div className="flex justify-between mb-6">
          <div>
            <div className="flex">
              <div className="flex items-center h-5">
                <input
                  {...register("remember")}
                  id="remember"
                  name="remember"
                  type="checkbox"
                  value=""
                  className="w-4 h-4 bg-gray-50 rounded border border-gray-300 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800"
                />
              </div>
              <label
                htmlFor="remember"
                className="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300"
              >
                Remember me
              </label>
            </div>
            {errors.remember ? (
              <span className="text-red-900">{errors.remember.message}</span>
            ) : (
              <></>
            )}
          </div>

          <div>
            <div>
              <label
                htmlFor="toggle"
                className="inline-flex relative items-center cursor-pointer"
              >
                <input
                  {...register("toggle")}
                  type="checkbox"
                  name="toggle"
                  value=""
                  id="toggle"
                  className="sr-only peer"
                />
                <div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
                <span className="ml-3 text-sm font-medium text-gray-900 dark:text-gray-300">
                  Accept
                </span>
              </label>
            </div>
            {errors.toggle ? (
              <span className="text-red-900">{errors.toggle.message}</span>
            ) : (
              <></>
            )}
          </div>
        </div>
Enter fullscreen mode Exit fullscreen mode

Whooaa with this, we're done with this result
validating checkbox and toggle

Thank you for following, incase you need the final code, here's the github repo

github repo link

Please do connect with me
linkedIn
youtube
Udemy

Top comments (2)

Collapse
 
cuginoale profile image
Alessio Carnevale • Edited

Hi Alaribe,
Interesting article! It's clear and goes deep into the implementation details
but... honestly, is all of that code really needed?
It feels a bit like we are reinventing the wheel whenever it comes to form validation/submission. There're tens of libraries doing pretty much the same thing and every time we work on a different project we have to learn how to use the library adopted by that project.

HTML5 already gives us built-in authentication function. Do we really need to use a 3rd party lib?

I am exploring a vanilla React approach here:
dev.to/cuginoale/form-validationsu...

and I would love to get your perspective on this!
cheers

Collapse
 
laribright profile image
Alaribe Bright

Great Alessio, I'll check it out and comment over there.
Thank you