Effortless Form Handling: Mastering the Art with React Hook Forms

Bawantha Rathnayaka
3 min readJan 21, 2024

Form handling is a crucial aspect of web development, and when it comes to building forms in a Next.js/React applications, using powerful tools like React Hook Form and Zod can greatly enhance your development experience. In this article, we’ll dive into the integration of React Hook Form with Zod to create a seamless and efficient form handling process in a React JS application.

Getting Started

Let’s start by understanding the key components of our form handling setup. The following code provides a basic template for a React application with a form that utilizes React Hook Form and Zod for validation.

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

const schema = z.object({
email: z.string().email(),
message: z.string().min(8),
});

type FormFields = z.infer<typeof schema>;

export default function App() {
const {
register,
handleSubmit,
setError,
formState: { errors, isSubmitting },
} = useForm<FormFields>({
defaultValues: {
email: "test@email.com",
},
resolver: zodResolver(schema),
});

const onSubmit: SubmitHandler<FormFields> = async (data) => {
try {
// Simulating an asynchronous operation (e.g., API request)
await new Promise((resolve) => setTimeout(resolve, 1000));
console.log(data);
} catch (error) {
// Setting an error for the 'email' field
setError("email", {
message: "This email is already taken",
});
}
};

return (
<div className="App">
<h1>React Hook Form with Zod</h1>

<form onSubmit={handleSubmit(onSubmit)}>
{/* Email input field */}
<input {...register("email")} type="text" placeholder="Email" />
{errors.email && <div>{errors.email.message}</div>}

{/* Message textarea */}
<textarea
{...register("message")}
name="message"
cols="30"
rows="10"
placeholder="Type your message here"
></textarea>
{errors.message && <div>{errors.message.message}</div>}

{/* Submit button */}
<button disabled={isSubmitting} type="submit">
{isSubmitting ? "Loading..." : "Submit"}
</button>

{/* Root-level error message */}
{errors.root && <div>{errors.root.message}</div>}
</form>
</div>
);
}

Breakdown of the Code

1. Schema Definition

We start by defining a Zod schema to validate our form fields. In this example, the schema includes an email field with email validation and a message field with a minimum length of 8 characters.

const schema = z.object({
email: z.string().email(),
message: z.string().min(8),
});

2. useForm Hook

The useForm hook from React Hook Form is employed to manage the form state and handle form submission. We pass our Zod schema to the resolver option to enable automatic validation.

const {
register,
handleSubmit,
setError,
formState: { errors, isSubmitting },
} = useForm<FormFields>({
defaultValues: {
email: "test@email.com",
},
resolver: zodResolver(schema),
});

3. Form Submission

The onSubmit function handles the form submission. It includes an asynchronous operation (simulated API request) and showcases how to set a specific error for the email field using setError.

const onSubmit: SubmitHandler<FormFields> = async (data) => {
try {
await new Promise((resolve) => setTimeout(resolve, 1000));
console.log(data);
} catch (error) {
setError("email", {
message: "This email is already taken",
});
}
};

4. Form Rendering

The actual form rendering includes input fields for email and message, along with error messages for each field. The submit button is disabled during the submission process, and a root-level error message is displayed if applicable.

<form onSubmit={handleSubmit(onSubmit)}>
{/* Email input field */}
<input {...register("email")} type="text" placeholder="Email" />
{errors.email && <div>{errors.email.message}</div>}

{/* Message textarea */}
<textarea
{...register("message")}
name="message"
cols="30"
rows="10"
placeholder="Type your message here"
></textarea>
{errors.message && <div>{errors.message.message}</div>}

{/* Submit button */}
<button disabled={isSubmitting} type="submit">
{isSubmitting ? "Loading..." : "Submit"}
</button>

{/* Root-level error message */}
{errors.root && <div>{errors.root.message}</div>}
</form>

Conclusion

In this article, we’ve explored the integration of React Hook Form and Zod for seamless form handling in a ReactJS application. Leveraging the capabilities of these libraries can significantly simplify the process of creating robust and efficient forms, offering a smooth user experience. Feel free to customize and extend this example to suit the specific requirements of your projects. Happy coding!

--

--

Bawantha Rathnayaka

I write about my experiences as a Software Engineer and the tech I use daily. portfolio - https://bawanthathilan.vercel.app/