In this section, let's walk through how you can use Zod and React Hook Form together to make form validation more powerful and flexible. We'll also see how to add some advanced validation like using refinement and regex for extra control.
What is React Hook Form?
React Hook Form (RHForm) is a library that helps manage forms in React. It handles input validation, form state, and error messages in a super simple way. By combining RHForm with Zod, we can create strong, easy-to-read validation logic.
Setting Up React Hook Form with Zod:
To integrate Zod with React Hook Form, you’ll need to install both libraries:
npm install react-hook-form zod @hookform/resolvers
Step 1: Create a Basic Form with Zod and React Hook Form
Let’s create a simple registration form that includes fields for name, email, and age. We’ll define a basic schema for our form:
import React from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
// Define your Zod schema
const schema = z.object({
name: z.string().min(2, "Name must be at least 2 characters long."),
email: z.string().email("Please enter a valid email."),
age: z.number().min(18, "You must be at least 18 years old.")
});
function RegistrationForm() {
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: zodResolver(schema)
});
const onSubmit = (data) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label>Name</label>
<input {...register("name")} />
{errors.name && <p>{errors.name.message}</p>}
</div>
<div>
<label>Email</label>
<input {...register("email")} />
{errors.email && <p>{errors.email.message}</p>}
</div>
<div>
<label>Age</label>
<input type="number" {...register("age")} />
{errors.age && <p>{errors.age.message}</p>}
</div>
<button type="submit">Register</button>
</form>
);
}
export default RegistrationForm;
Explanation:
- useForm: Hook from React Hook Form for managing form state.
- zodResolver: Connects Zod with React Hook Form for validation.
- schema: Defines the validation rules for the form fields.
Step 2: Adding Refinement and Regex Validation
Now, let’s enhance our form validation using refinement and regex patterns.
Name Validation Using Regex
We’ll ensure that the name only contains alphabetic characters (no numbers or special characters). Here’s how we can use regex for that:
const schema = z.object({
name: z.string()
.min(2, "Name must be at least 2 characters long.")
.regex(/^[A-Za-z\s]+$/, "Name should only contain letters and spaces."),
email: z.string()
.email("Please enter a valid email."),
age: z.number()
.min(18, "You must be at least 18 years old.")
});
regex(/^[A-Za-z\s]+$/): This regular expression checks that the name contains only letters (both upper and lower case) and spaces. If it contains anything else, the error message "Name should only contain letters and spaces." will be displayed.
Email Validation with Refinement
Next, let’s refine our email validation. We’ll restrict it to only accept emails ending with a specific domain, like @example.com:
const schema = z.object({
name: z.string()
.min(2, "Name must be at least 2 characters long.")
.regex(/^[A-Za-z\s]+$/, "Name should only contain letters and spaces."),
email: z.string()
.email("Please enter a valid email.")
.refine((value) => value.endsWith('@example.com'), {
message: "Email must end with @example.com",
}),
age: z.number()
.min(18, "You must be at least 18 years old.")
});
refine(): This method allows you to add custom logic for validation. Here, we check if the email ends with @example.com. If it doesn’t, the user will see the message "Email must end with @example.com."
Age Validation with Refinement:
Lastly, let’s add a rule to ensure the age is an even number:
const schema = z.object({
name: z.string()
.min(2, "Name must be at least 2 characters long.")
.regex(/^[A-Za-z\s]+$/, "Name should only contain letters and spaces."),
email: z.string()
.email("Please enter a valid email.")
.refine((value) => value.endsWith('@example.com'), {
message: "Email must end with @example.com",
}),
age: z.number()
.min(18, "You must be at least 18 years old.")
.refine((value) => value % 2 === 0, {
message: "Age must be an even number."
})
});
refine(value % 2 === 0): This line checks if the age is even by checking if it’s divisible by 2. If the user enters an odd number, they will receive the message "Age must be an even number."
Step 3: Handling Errors in the Form
Now that we’ve added these additional validation rules, we need to ensure errors are displayed appropriately in the form. Here’s how you can display error messages for each field:
{errors.name && <p>{errors.name.message}</p>}
{errors.email && <p>{errors.email.message}</p>}
{errors.age && <p>{errors.age.message}</p>}
By integrating Zod with React Hook Form, you create a powerful validation system that can handle complex rules:
- Regex validation allows you to enforce specific patterns (like letters only for names).
- Refinement provides the flexibility to implement custom rules (like checking email domains and ensuring age is even).
This combination makes your forms not only more robust but also more user-friendly, ensuring that the data collected meets your requirements. Happy coding!