[React JS] Getting started with react-hook-form and Zod. For better form handling
Alfi Samudro Mulyo
Posted on February 6, 2024
At the beginning of working with React, I had difficulty handling many inputs in a form with validation. I create a different state for every input element, so when there are more and more input elements, the number of states also increases, making it painful to maintain.
But now, all those difficulties are gone since I found React Hook Form and zod.
React Hook Form reduces the amount of code you need to write while removing unnecessary re-renders and it works really well with Zod. Zod is a TypeScript-first schema declaration and validation library.
Preparation
Please take note that the code below is written in typescript.
All we need to do is just install these three packages:
npm install zod
npm install react-hook-form
npm install @hookform/resolvers
to your React project.
Let's dive into the code.
First of all, we need to import the packages mentioned above:
import { useForm, SubmitHandler } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
Create schema and validation rules using Zod:
const schema = z.object({
name: z.string().min(1, { message: "Name is required." }),
email: z
.string()
.min(1, { message: "Email is required." })
.email({ message: "Please enter a valid email." }),
password: z
.string()
.min(8, { message: "Password must be at least 8 characters." })
.regex(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/,
{
message:
"Password must contain at least one uppercase, one lowercase, one number, one special character",
}
),
});
This is the beauty of Zod as it simplifies schema validation effortlessly. In the provided code, we define three attributes along with their respective validation rules, signifying that our form will encompass three distinct inputs.
Since we're using typescript, let's convert the schema above into type so we have typesafe for every part of our code moving on:
type FormFields = z.infer<typeof schema>;
By assigning
FormFields
to React Hook Form, we'll have beautiful typesafe
We've done with schema. Now, let's move on to React function component. In the previous steps, we already created schema
and FormFields
so we can attach them to React Hook Form:
const {
register,
handleSubmit,
setError,
formState: { errors, isSubmitting },
} = useForm<FormFields>({
resolver: zodResolver(schema),
});
register
: In React Hook Form, the register function is a crucial method that allows you to register your input components with the form. By doing so, you inform the form about the existence of your input fields, enabling the library to manage their state and validation.
handleSubmit
: The handleSubmit function in React Hook Form is a utility provided by the library to simplify the form submission process. It handles the form validation, prevents the default form submission behavior, and executes the provided callback function with the validated form data.
setError
: A function used to manually set an error for a specific field. This can be useful in scenarios where you want to trigger or display a custom error message based on certain conditions.
formState
: An object that provides information about the state of the form. It is typically used to access information such as whether the form is submitting, submitted, or if there are any validation errors, etc.
Here is the code:
function Home() {
const {
register,
handleSubmit,
setError,
formState: { errors, isSubmitting },
} = useForm <
FormFields >
{
resolver: zodResolver(schema),
};
// FUNCTION TO HANDLE FORM SUBMISSION
const onSubmit: SubmitHandler<FormFields> = async (data) => {
try {
await new Promise((resolve) => setTimeout(resolve, 1000));
// throw new Error("Email is already taken.");
alert(JSON.stringify(data));
} catch (error) {
// SET ERROR MESSAGE IF ANY ERROR OCCURS
setError("email", {
message: "Email is already taken.",
});
}
};
return (
<div className="flex flex-col items-center justify-center h-screen">
<h1 className="text-2xl mb-4">Register Form</h1>
<form
onSubmit={handleSubmit(onSubmit)}
className="flex flex-col gap-2 w-96"
>
<input
// REGISTER INPUT FIELD
{...register("name")}
className="border w-full rounded-md p-4"
type="text"
placeholder="Name"
/>
{/* SHOW ERROR MESSAGE IF OCCURED */}
{errors.name && (
<span className="text-xs text-red-600 font-semibold">
{errors.name.message}
</span>
)}
<input
// REGISTER INPUT FIELD
{...register("email")}
className="border w-full rounded-md p-4"
type="text"
placeholder="Email"
/>
{/* SHOW ERROR MESSAGE IF OCCURED */}
{errors.email && (
<span className="text-xs text-red-600 font-semibold">
{errors.email.message}
</span>
)}
<input
// REGISTER INPUT FIELD
{...register("password")}
className="border w-full rounded-md p-4"
type="password"
placeholder="Password"
/>
{/* SHOW ERROR MESSAGE IF OCCURED */}
{errors.password && (
<span className="text-xs text-red-600 font-semibold">
{errors.password.message}
</span>
)}
<button
type="submit"
disabled={isSubmitting} // DISABLE BUTTON IF FORM IS SUBMITTING
className="bg-blue-500 text-white p-2 rounded disabled:bg-gray-300 disabled:cursor-not-allowed"
>
{isSubmitting ? "Loading..." : "Register"}{" "}
{/* SHOW LOADING TEXT IF FORM IS SUBMITTING */}
</button>
</form>
</div>
);
}
Full code
That's all. No need to create a bunch of states anymore. If the inputs keep growing, we can just add them to the schema and let the React Hook Form + Zod do the magic.
Posted on February 6, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
February 6, 2024