How to Handle Forms and Validation in React with React Hook Form and Zod

Forms are essential to most web applications, whether you're collecting user information, managing logins, or processing payments. However, managing forms efficiently in React can be challenging, especially regarding validation. React Hook Form is a powerful library that simplifies form handling. Combined with Zod, a schema validation library, it makes building and validating forms easy and reliable.

This step-by-step tutorial will show you how to handle forms and validation in React using React Hook Form and Zod.

Step 1: Setting Up React Hook Form

Before we begin, make sure you have a React project ready. If you don't, you can create one using:

npx create-react-app form-validation-tutorial
cd form-validation-tutorial

Next, install React Hook Form and Zod by running the following command:

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

Step 2: Creating a Simple Form

Let’s start by creating a simple form with fields like email and password. In your App.js (or App.tsx for TypeScript) file, add the following:

import React from "react";
import { useForm } from "react-hook-form";

const App = () => {
  const { register, handleSubmit } = useForm();

  const onSubmit = (data) => {
    console.log("Form Data:", data);
  };

  return (
    <div>
      <h1>Simple Form</h1>
      <form onSubmit={handleSubmit(onSubmit)}>
        <div>
          <label>Email:</label>
          <input {...register("email")} type="email" />
        </div>
        <div>
          <label>Password:</label>
          <input {...register("password")} type="password" />
        </div>
        <button type="submit">Submit</button>
      </form>
    </div>
  );
};

export default App;

How it works:

  • useForm() provides methods like register and handleSubmit.

  • register connects form inputs to React Hook Form's internal state.

  • handleSubmit handles the form submission process.


Step 3: Adding Validation with Zod

Zod helps us define a schema for form validation. To integrate it with React Hook Form, follow these steps:

  1. Import zod and the zodResolver from @hookform/resolvers.

  2. Define a schema using Zod.

  3. Pass the schema to the useForm hook.

Here’s the updated code:

import React from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";

const schema = z.object({
  email: z.string().email("Invalid email address"),
  password: z.string().min(6, "Password must be at least 6 characters"),
});

const App = () => {
  const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: zodResolver(schema),
  });

  const onSubmit = (data) => {
    console.log("Form Data:", data);
  };

  return (
    <div>
      <h1>Form with Validation</h1>
      <form onSubmit={handleSubmit(onSubmit)}>
        <div>
          <label>Email:</label>
          <input {...register("email")} type="email" />
          {errors.email && <p>{errors.email.message}</p>}
        </div>
        <div>
          <label>Password:</label>
          <input {...register("password")} type="password" />
          {errors.password && <p>{errors.password.message}</p>}
        </div>
        <button type="submit">Submit</button>
      </form>
    </div>
  );
};

export default App;

How it works:

  • The schema defines validation rules for each field.

  • zodResolver connects Zod to React Hook Form.

  • formState.errors contains validation errors for each field.


Step 4: Handling Errors

We’ve already added error messages in the form, but let’s explain it in detail:

  • Use errors.email or errors.password to check if there’s an error in the respective field.

  • Use {errors.fieldName.message} to display the error message defined in the Zod schema.

For example:

{errors.email && <p>{errors.email.message}</p>}

Step 5: Submitting the Form

When the form is valid, the onSubmit function is called. You can process the form data here by sending it to an API or saving it locally.

Here’s how you can simulate submitting data to an API:

const onSubmit = async (data) => {
  try {
    const response = await fetch("https://api.example.com/submit", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(data),
    });
    console.log("API Response:", await response.json());
  } catch (error) {
    console.error("Error submitting form:", error);
  }
};

Step 6: Best Practices

  1. Use Default Values: Initialize your form fields with default values.
const { register, handleSubmit } = useForm({
  defaultValues: { email: "", password: "" },
});
  1. Keep Forms Simple: Avoid adding too many fields in one form. Split long forms into steps.

  2. Error Feedback: Display clear and specific error messages for a better user experience.

  3. Accessibility: Add appropriate aria attributes to inputs for better accessibility.

Using React Hook Form with Zod makes handling forms in React easy. React Hook Form efficiently manages form state, while Zod simplifies validation with its powerful schema definition. Together, they allow developers to build robust and user-friendly forms with minimal effort.

Now that you know how to handle forms and validation, you can further explore features like multi-step forms, file uploads, or integrating authentication systems.

Happy coding! 🎉

Â