Table Of Contents
React Hook Form and Yup: Validations made perfect
Milan Poudel •
November 28, 2022
Table Of Contents
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.
Made with ❤ by
Milan Poudel