Zod with React Hooks
This setup allows you to validate your form inputs using Zod’s schema validation while leveraging RHF’s form management.
Step-by-Step Guide to Zod and React Hook Form Integration
1. Install Necessary Dependencies
You need to install React Hook Form, Zod, and a resolver that connects the two.
npm install react-hook-form zod @hookform/resolvers
react-hook-form
handles form state and submission.zod
is used for schema validation.@hookform/resolvers
provides a bridge between Zod and React Hook Form.
2. Define the Zod Schema
Create a Zod schema that defines the structure and validation rules for your form data.
import { z } from 'zod';
const formSchema = z.object({
name: z.string().min(2, "Name must be at least 2 characters long"),
email: z.string().email("Invalid email address"),
age: z.number().min(18, "You must be at least 18 years old"),
});
In this schema:
name
is a string with a minimum length of 2 characters.email
must be a valid email address.age
is a number with a minimum value of 18.
3. Set Up React Hook Form with Zod Resolver
Now, create the form using React Hook Form. The zodResolver
from @hookform/resolvers/zod
will connect the Zod schema to RHF, so validation is handled by Zod.
import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
// Define the Zod schema
const formSchema = z.object({
name: z.string().min(2, "Name must be at least 2 characters long"),
email: z.string().email("Invalid email address"),
age: z.number().min(18, "You must be at least 18 years old"),
});
type FormData = z.infer<typeof formSchema>;
export default function MyForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormData>({
resolver: zodResolver(formSchema), // Use Zod resolver
});
const onSubmit = (data: FormData) => {
console.log("Form data:", data); // Handle form submission
};
return (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
{/* Name field */}
<div>
<label
htmlFor="name"
className="block text-sm font-medium text-gray-700"
>
Name:
</label>
<input
{...register("name")}
id="name"
className="mt-1 block w-full rounded-md border border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
/>
{errors.name && (
<p className="mt-1 text-sm text-red-600">{errors.name.message}</p>
)}
</div>
{/* Email field */}
<div>
<label
htmlFor="email"
className="block text-sm font-medium text-gray-700"
>
Email:
</label>
<input
{...register("email")}
id="email"
className="mt-1 block w-full rounded-md border border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
/>
{errors.email && (
<p className="mt-1 text-sm text-red-600">{errors.email.message}</p>
)}
</div>
{/* Age field */}
<div>
<label
htmlFor="age"
className="block text-sm font-medium text-gray-700"
>
Age:
</label>
<input
type="number"
{...register("age", { valueAsNumber: true })}
id="age"
className="mt-1 block w-full rounded-md border border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
/>
{errors.age && (
<p className="mt-1 text-sm text-red-600">{errors.age.message}</p>
)}
</div>
<button
type="submit"
className="w-full py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Submit
</button>
</form>
);
}
Explanation of Key Components:
useForm
Hook with Zod Resolver:
useForm
is used to initialize the form with thezodResolver
, which automatically applies the Zod schema for validation.register
is used to bind the form fields to React Hook Form's internal state management.handleSubmit
is used to trigger validation and form submission.errors
is populated if there are any validation issues (directly from Zod).
- Form Inputs: Each input field is registered using
register("fieldName")
, which tracks the input state and links it to the form's state in React Hook Form. For example:
register("name")
links thename
input to the form state.- If a validation error occurs, the
errors
object will contain the error message generated by Zod.
- Error Handling: If Zod’s validation fails for any field, an error message is stored in
errors[fieldName].message
. This message is rendered below each form field when there's a validation error.
Example Breakdown:
- If the user enters fewer than 2 characters for
name
, the Zod schema will trigger an error, and"Name must be at least 2 characters long"
will appear under the input field. - If the email is not a valid format (e.g., missing
@
), Zod will generate the error"Invalid email address"
, and this will be displayed. - If the user enters an
age
below 18, Zod will trigger the error"You must be at least 18 years old"
.
4. Final Form Functionality
- When the form is submitted, the
onSubmit
function is triggered if all fields pass validation. - The form’s data is printed to the console, or you can further process the data as needed.
Key Points:
- Schema-Driven Validation: Zod handles all validation based on the schema you define.
- Integration: RHF manages form state and submissions, while Zod checks the form data against the schema.
- Errors: Validation errors from Zod are displayed in the form using React Hook Form’s
errors
object.
This combination provides a powerful way to manage forms with clear, schema-driven validation rules while maintaining a clean and efficient React form structure.
github reference: https://github.com/abdahad1996/ReactLearning