Form validation used to be my least favorite part of frontend development. Between managing state with useState, handling errors, and ensuring type safety, it felt like I was writing more boilerplate than actual logic. Then I discovered React Hook Form combined with Zod, and everything changed.
What makes React Hook Form so powerful isn't just that it reduces codeβit's that it makes forms actually enjoyable to build. React Hook Form uses the useForm hook to handle all the performance optimizations (minimal re-renders, uncontrolled components), while Zod gives you runtime validation that matches your TypeScript types perfectly.
Why React Hook Form?
- Minimal re-renders: Uses uncontrolled components with refs
- Small bundle size: ~9KB (gzipped)
- Excellent TypeScript support: Full type inference with Zod
- Built-in validation: Works seamlessly with validation libraries
- Better performance: Faster than alternatives like Formik
Installation
npm install react-hook-form @hookform/resolvers zod
Building Your First Schema
Let's start with a real-world example: a product form:
import * as z from "zod";
const productSchema = z.object({
name: z
.string()
.trim()
.min(1, { message: "Required" })
.min(2, { message: "Minimum 2 characters required" }),
categoryId: z.string().trim().min(1, { message: "Required" }),
price: z.string().trim().min(1, { message: "Required" }),
stock: z.string().trim().min(1, { message: "Required" }),
product_image: z.preprocess((val) => {
if (!val) return null;
if (val instanceof FileList) {
const file = val.item(0);
return file ?? null;
}
return val;
}, z.instanceof(File).nullable().refine(
(file) => file !== null,
{ message: "Product image is required" }
)),
});
type ProductFormData = z.infer<typeof productSchema>;
Complete Form Example
Here's a production-ready React Hook Form component:
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useState } from "react";
function AddProduct() {
const [submitError, setSubmitError] = useState<string | null>(null);
const {
register,
handleSubmit,
watch,
reset,
formState: { errors, isSubmitting, isValid },
} = useForm<ProductFormData>({
defaultValues: {
name: "",
categoryId: "",
price: "",
stock: "",
product_image: null,
},
resolver: zodResolver(productSchema),
mode: "all",
});
const productImage = watch("product_image");
const onSubmit = async (data: ProductFormData) => {
try {
setSubmitError(null);
const formData = new FormData();
formData.append("name", data.name.trim());
if (data.product_image) {
formData.append("product_image", data.product_image);
}
const response = await fetch("/api/products", {
method: "POST",
body: formData,
});
if (!response.ok) {
throw new Error("Failed to create product");
}
reset();
} catch (error) {
setSubmitError(error instanceof Error ? error.message : "An error occurred");
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("name")} placeholder="Product Name" />
{errors.name && <p>{errors.name.message}</p>}
<input
type="file"
accept="image/*"
{...register("product_image")}
/>
{errors.product_image && <p>{errors.product_image.message}</p>}
<button type="submit" disabled={isSubmitting || !isValid}>
{isSubmitting ? "Creating..." : "Create Product"}
</button>
</form>
);
}
Best Practices
- Use Zod schemas for validation
- Leverage type inference from Zod
- Handle file uploads properly
- Show validation errors appropriately
- Use watch for dependent fields
- Reset forms after successful submission
π Read the Complete Guide
This is just a brief overview! The complete guide on my blog includes:
- β Advanced Validation - Custom validators, async validation
- β File Uploads - Single and multiple file handling
- β Form Arrays - Dynamic form fields
- β Dependent Fields - Conditional validation
- β Error Handling - Complete error management patterns
- β Performance Optimization - Minimizing re-renders
- β Real-world examples from production applications
π Read the full article with all code examples here
What's your experience with React Hook Form? Share your tips in the comments! π
For more React guides, check out my blog covering TypeScript, React Router, Redux Toolkit, and more.
Top comments (0)