Back to articles

Level Up Your React Forms with React Hook Form and Zod Validation

AuthorMajd Muhtaseb06/08/20257 minutes
Level Up Your React Forms with React Hook Form and Zod Validation

Introduction

Form validation is crucial for any web application. While React offers various form libraries, React Hook Form stands out for its performance and ease of use. Combining it with Zod, a TypeScript-first schema validation library, gives you type-safe and robust validation.

Why React Hook Form and Zod?

  • React Hook Form: Minimal re-renders, easy to integrate, and performant.
  • Zod: TypeScript-first, declarative schema definition, and excellent error handling.

Setting up the Project

First, create a new React project (if you don't have one already):

npx create-react-app my-form-app
cd my-form-app

Then, install the necessary packages:

npm install react-hook-form zod @hookform/resolvers

Defining the Schema with Zod

Let's define a simple form schema for a user registration form:

import * as z from "zod";

const userSchema = z.object({
  firstName: z.string().min(2, { message: "First name must be at least 2 characters." }),
  lastName: z.string().min(2, { message: "Last name must be at least 2 characters." }),
  email: z.string().email({ message: "Invalid email address." }),
  password: z.string().min(8, { message: "Password must be at least 8 characters." }),
  confirmPassword: z.string(),
}).refine((data) => data.password === data.confirmPassword, {
  message: "Passwords don't match",
  path: ["confirmPassword"], // path of error
});

export type UserSchemaType = z.infer<typeof userSchema>;

This schema defines the structure of our form data and specifies validation rules for each field. The refine method adds a custom validation to ensure the password and confirm password fields match.

Implementing the Form with React Hook Form

Now, let's create the React component using React Hook Form and the Zod resolver:

import React from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from "zod";
import {userSchema, UserSchemaType} from './schema';

const RegistrationForm = () => {
  const { register, handleSubmit, formState: { errors } } = useForm<UserSchemaType>({
    resolver: zodResolver(userSchema),
    defaultValues: {
      firstName: "",
      lastName: "",
      email: "",
      password: "",
      confirmPassword: "",
    }
  });

  const onSubmit = (data: UserSchemaType) => {
    console.log("Form Data:", data);
    alert(JSON.stringify(data)); // Replace with your actual submission logic
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label htmlFor="firstName">First Name:</label>
        <input type="text" id="firstName" {...register("firstName")} />
        {errors.firstName && <p>{errors.firstName.message}</p>}
      </div>

      <div>
        <label htmlFor="lastName">Last Name:</label>
        <input type="text" id="lastName" {...register("lastName")} />
        {errors.lastName && <p>{errors.lastName.message}</p>}
      </div>

      <div>
        <label htmlFor="email">Email:</label>
        <input type="email" id="email" {...register("email")} />
        {errors.email && <p>{errors.email.message}</p>}
      </div>

      <div>
        <label htmlFor="password">Password:</label>
        <input type="password" id="password" {...register("password")} />
        {errors.password && <p>{errors.password.message}</p>}
      </div>

       <div>
        <label htmlFor="confirmPassword">Confirm Password:</label>
        <input type="password" id="confirmPassword" {...register("confirmPassword")} />
        {errors.confirmPassword && <p>{errors.confirmPassword.message}</p>}
      </div>

      <button type="submit">Register</button>
    </form>
  );
};

export default RegistrationForm;

Key points:

  • useForm hook initializes the form and provides methods for managing form state and validation.
  • zodResolver connects React Hook Form with our Zod schema.
  • register function is used to register input fields and connect them to the form state.
  • formState.errors contains validation errors from Zod.

Conclusion

React Hook Form and Zod provide a powerful combination for building robust and type-safe forms in React. This approach streamlines form validation, improves code quality, and enhances the user experience.