How to Build a React Form Using React Hook Form and Yup Validation: A Step-by-Step Guide
Introduction
React Hook Form is a powerful tool that simplifies form handling in React. In combination with Yup
, a validation library, you can create robust, validated forms easily. Whether you're a beginner or just need a refresher, this guide will walk you through the process step-by-step, providing all the examples and explanations you need to build a working sign-in form with validation.
Why Use React Hook Form?
React Hook Form focuses on providing the best performance and reducing unnecessary re-renders, making it a lightweight alternative to other form libraries. When you add Yup
for schema validation, it becomes a strong tool to handle complex validations in a clean and scalable way.
Key Features:
- Simple form handling with minimal code.
- Built-in support for validation.
- Reduced re-renders to enhance performance.
- Easy integration with Yup for schema-based validation.
Let’s start building the form!
Step 1: Install Necessary Packages
Before you start coding, you need to install the required libraries: react-hook-form
, @hookform/resolvers
, and Yup
.
Run the following command in your project:
npm install react-hook-form @hookform/resolvers yup
-
react-hook-form
: Provides hooks for handling forms in React. -
@hookform/resolvers
: ConnectsYup
withreact-hook-form
. -
Yup
: A JavaScript schema builder for value parsing and validation.
Step 2: Define the Form Inputs
Create the input fields that will be part of the form. For a basic sign-in form, we'll have two fields: email and password.
Example of the Input Interface:
// Define the structure of form inputs
interface IFormInput {
email?: string;
password?: string;
}
Here, we define an interface IFormInput
that includes optional properties for email
and password
. This interface ensures type safety when handling form data.
Step 3: Create a Dynamic Validation Schema with Yup
For validation, we'll use Yup to define rules for each input. You can customize this schema as needed. Here’s how to dynamically generate it based on field configurations.
Sign-In Field Configurations:
// Configurations for each field's validation
export const signInfieldConfigs = {
email: {
type: "string",
required: "Email is required",
matches: {
regex: /^[\w.-]+@[\w-]+\.[\w-]{2,4}$/,
message: "Please enter a valid email address",
},
},
password: {
type: "string",
required: "Password is required",
matches: {
regex:
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{6,20}$/,
message: "Password must contain at least one uppercase, one lowercase, one number, and one special character",
},
min: {
value: 6,
message: "Password must be at least 6 characters",
},
max: {
value: 20,
message: "Password must not exceed 20 characters",
},
},
};
These configurations define the validation rules for email
and password
fields, such as regex patterns, required fields, and length restrictions.
Generate Validation Schema:
import * as Yup from "yup";
export const generateValidationSchema = (fieldConfigs: any) => {
const shape: any = {};
Object.keys(fieldConfigs).forEach((key) => {
const config = fieldConfigs[key];
let fieldSchema = Yup.string(); // Default to string type
if (config.required) {
fieldSchema = fieldSchema.required(config.required);
}
if (config.matches && config.matches.regex) {
fieldSchema = fieldSchema.matches(config.matches.regex, config.matches.message);
}
if (config.min) {
fieldSchema = fieldSchema.min(config.min.value, config.min.message);
}
if (config.max) {
fieldSchema = fieldSchema.max(config.max.value, config.max.message);
}
shape[key] = fieldSchema;
});
return Yup.object().shape(shape);
};
Here, the generateValidationSchema
function dynamically creates a Yup schema based on the configurations provided for each field.
Step 4: Set Up React Hook Form
Now, integrate react-hook-form
with the validation schema.
import { SubmitHandler, useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
// Create the form component
const SignIn = () => {
const validationSchema = generateValidationSchema(signInfieldConfigs);
// Initialize the form with useForm hook
const {
register,
handleSubmit,
formState: { errors },
} = useForm<IFormInput>({
resolver: yupResolver(validationSchema),
});
// Handle form submission
const onSubmit: SubmitHandler<IFormInput> = (data) => {
console.log(data);
};
return (
<div className="form-container">
<form onSubmit={handleSubmit(onSubmit)}>
{/* Email Field */}
<input
{...register("email")}
placeholder="Email Address"
/>
{errors.email && <p>{errors.email.message}</p>}
{/* Password Field */}
<input
{...register("password")}
type="password"
placeholder="Password"
/>
{errors.password && <p>{errors.password.message}</p>}
{/* Submit Button */}
<button type="submit">Sign In</button>
</form>
</div>
);
};
export default SignIn;
Explanation:
- The
useForm
hook initializes form management with aresolver
for validation. -
register
is used to register form inputs with React Hook Form. -
handleSubmit
handles form submission, calling theonSubmit
function when the form is valid. - Validation errors are accessible through
formState.errors
, which is automatically populated if validation fails.
Step 5: Display Validation Errors
In this step, ensure that any validation errors are displayed to the user in an understandable way. Here, we use a custom component InputError
to show the errors.
const InputError = ({ error }: { error?: string }) => {
return error ? <p className="error-text">{error}</p> : null;
};
The InputError
component simply takes the error message and displays it when it exists.
Step 6: Add CSS for Better UI
Make sure to add some styles to improve the user experience:
.form-container {
max-width: 500px;
margin: auto;
padding: 2rem;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
input {
width: 100%;
padding: 0.8rem;
margin-bottom: 1rem;
border-radius: 4px;
border: 1px solid #ccc;
}
button {
width: 100%;
padding: 0.8rem;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}
.error-text {
color: red;
font-size: 0.9rem;
}
Step 7: Test the Form
Test your form by entering invalid and valid data to ensure that the validation messages are shown correctly. For example:
- If you enter an invalid email, the message "Please enter a valid email address" should appear.
- If you enter a password that doesn't meet the criteria, you'll see "Password must contain at least one uppercase, one lowercase, one number, and one special character."
FAQs
What Happens if Validation Fails?
If validation fails, the errors
object inside formState
is populated with the corresponding error messages for each field. These errors can be displayed to the user.
Can I Add More Fields?
Yes, you can add more fields to the form and update the validation schema accordingly. For example, you could add a username
field with its own validation rules.
Conclusion
You’ve now successfully created a sign-in form in React using react-hook-form
and Yup
for validation. This form handles validation dynamically and can be easily extended to include more fields. By following the steps above, even beginners can create complex forms with minimal effort and ensure a smooth user experience.