Table Of Contents


React Hook Form and Yup: Validations made perfect

React Hook Form and Yup: Validations made perfect

profile-image

Milan Poudel

November 28, 2022

Form validation is easy when there are few fields. When there is a form with lots of input fields and a multistep one, validating each field will be a tough task. But there's fortunately validation libraries like formik and react hook form which have really made complex validations easier. In this post, we will see how we can use react hook form and yup to validate a form.


Setting up React Project with Vite


Let's get started with creating a new react project. I will use a vite to create a react project with typescript. Make sure you choose React and Typescript as variants while initializing.


npm create vite@latest


Next step is creating a form UI. We will create a folder named pages inside src that is generated by default. We will create a simple UI since our focus is mostly on the form validation. Also don't forget to install all packages that are generated before starting our development server.



// to install all packages
npm install

//to start our server
npm run dev

//install react router dom library.
npm install react-router-dom



Creating our pages routes


Let's install react routing library and let's create a new route named "/signup" in our Root App file


//src/app.tsx
import { Routes, Route, BrowserRouter } from "react-router-dom";
import "./App.css";
import SignUp from "./pages/SignUp";

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/signup" element={<SignUp />} />
      </Routes>
    </BrowserRouter>
  );
}
export default App;


Our next step is creating a sign up form UI. We will use the following jsx


// src/pages/SignUp.tsx

const SignUp = () => {
  const submitHandler= (e: React.FormEvent) => {
    e.preventDefault();
  };
  const genders = ["Male", "Female", "Other"];
  const roles = ["Student", "Teacher", "Admin"];


  return (
    <div className="container">
      <form className="form" onSubmit={handleSubmit}>
        <h1>Create an account</h1>
        <input type="text" placeholder="Name" className="form-field" />
        <input type="email" placeholder="Email" className="form-field" />
        <input type="phone" placeholder="Phone" className="form-field" />
        <select className="form-field">
          <option value="">Select Gender</option>
          {genders?.map((gender) => (
            <option value="Male">{gender}</option>
          ))}
        </select>
        <div className="radio-container">
          <p>Role:</p>
          {roles?.map((role) => (
            <div>
              <input type="radio" name="role" value={role} key={role} />
              <label>{role}</label>
            </div>
          ))}
        </div>
        <input
          type="password"
          placeholder="Password"
          required
          className="form-field"
        />
        <input
          type="password"
          placeholder="Confirm Password"
          required
          className="form-field"
        />
        <button type="submit" className="form-button">
          Submit
        </button>
      </form>
    </div>
  );
};

export default SignUp;


Styling our SignUp Form


Here's the style css of our form.


// App.css

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html,
body {
  font-size: 16px;
  line-height: 1.6;
}

.container {
  height: 100vh;
  width: 100vw;
  display: flex;
  justify-content: center;
  align-items: center;
  background: pink;
}


.container .form {
  padding: 40px;
  background: #fff;
  border-radius: 5px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  width: 470px;
  margin: 0 auto;
  text-align: center;
  height: 570px;
  overflow: auto;
}


.form h1 {
  margin-bottom: 20px;
  color: #333;
  font-size: 24px;
  font-weight: 700;
}


.form-field {
  margin-bottom: 20px;
  border: 1px solid #ccc;
  border-radius: 5px;
  width: 100%;
  padding: 10px 0px;
  padding-left: 14px;
}


.form-button {
  background: #333;
  color: #fff;
  border: none;
  border-radius: 5px;
  padding: 10px 20px;
  cursor: pointer;
}


.radio-container {
  display: flex;
  justify-content: center;
  align-items: center;
  margin-bottom: 20px;
}


.radio-container label {
  margin-right: 10px;
}


.radio-container p {
  margin-right: 20px;
}


With this, we will import react hook form and register our inputs with it. Let's install it first.


npm install react-hook-form


Integrating React Hook Form:


Now let's use it in all our input fields.


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

const ErrorMessage = ({ message }: { message: string }) => (
  <p style={{ color: "red", fontSize: "14px" }}>{message}</p>
);

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


  const genders = ["Male", "Female", "Other"];
  const roles = ["Student", "Teacher", "Admin"];


  const submitHandler = (data: FieldValues) => {
    console.log(data);
  };


  return (
    <div className="container">
      <form className="form" onSubmit={handleSubmit(submitHandler)}>
        <h1>Create an account</h1>
        <input
          type="text"
          placeholder="Name"
          className="form-field"
          {...register("name", { required: "Name is required" })}
        />
        {errors.name && (
          <ErrorMessage message={errors.name.message as string} />
        )}


        <input
          type="email"
          placeholder="Email"
          className="form-field"
          {...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 && (
          <ErrorMessage message={errors.email.message as string} />
        )}


        <input
          type="phone"
          placeholder="Phone"
          className="form-field"
          {...register("phone", {
            required: "Phone is required",
            pattern: {
              value: /^[0-9]{10}$/i,
              message: "invalid phone number",
            },
          })}
        />
        {errors.phone && (
          <ErrorMessage message={errors.phone.message as string} />
        )}
        <select
          className="form-field"
          {...register("gender", { required: "Gender is required" })}
        >
          <option value="">Select Gender</option>
          {genders?.map((gender) => (
            <option value="Male">{gender}</option>
          ))}
        </select>
        {errors?.gender && (
          <ErrorMessage message={errors?.gender?.message as string} />
        )}
        <div className="radio-container">
          <p>Role:</p>
          {roles?.map((role) => (
            <div>
              <input
                type="radio"
                value={role}
                key={role}
                {...register("role")}
              />
              <label>{role}</label>
            </div>
          ))}
        </div>
        {errors?.role && (
          <ErrorMessage message={errors?.role?.message as string} />
        )}
        <input
          type="password"
          placeholder="Password"
          className="form-field"
          {...register("password", {
            required: "Password is required",
            minLength: {
              value: 8,
              message: "Password must have at least 8 characters",
            },
          })}
        />
        {errors.password && (
          <ErrorMessage message={errors.password.message as string} />
        )}
        <input
          type="password"
          placeholder="Confirm Password"
          className="form-field"
          {...register("confirmPassword", {
            required: "Confirm Password is required",
            validate: (value) =>
              value === "password" || "The passwords do not match",
          })}
        />
        {errors.confirmPassword && (
          <ErrorMessage message={errors.confirmPassword.message as string} />
        )}
        <button type="submit" className="form-button">
          Submit
        </button>
      </form>
    </div>
  );
};

export default SignUp;


What we did:


  • We imported register, handleSubmit and formState and destructure our errors.


  • We created a new component named "ErrorText" to show our validation errors.


  • We registered our form fields with RHK's register so it will handle our "value" and "onChangeField". We don't need to worry about that.


  • We replaced onSubmit event of Form event with "handleSubmit" provided by RHK which will also contain our form data.


  • We added "regex" pattern to validate the email address and phone number format.


With this. when we submit a form, we will see validation errors and our form submission will be stopped.


Well, we setup patterns and validations messages here, it's okay but we can do better by moving all our validations and pattern somewhere else, so here yup comes into play.

Programming | Coding | Learning

Subscribe to learn about new technology and updates. Join over 1000+ members community to stay up to date with latest articles.

© 2024 Code With Milan. All rights reserved.