How to Build Client-Side Form Validation in Next.js with React Hook Form & Zod
NextJS

How to Build Client-Side Form Validation in Next.js with React Hook Form & Zod

Build a Form with Client-Side Validation in Next.js using Zod

Author
Richard Mendes
April 06, 2026 • 5 mins

Install Dependencies

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

Create Validation Schema with Zod, Build the Form Component

"use client";
import Link from "next/link";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";

const formSchema = z.object({
name: z.string().min(1, "Name is required"),
email: z.string().email("Invalid email address"),
username: z.string().min(1, "Username is required"),
password: z.string().min(6, "Password must be at least 6 characters"),
confirmPassword: z.string().min(6, "Password must be at least 6 characters"),
}).refine((data) => data.password === data.confirmPassword, {
message: "Passwords do not match",
path: ["confirmPassword"],
});

type FormData = z.infer<typeof formSchema>;





export default function CreateUserPage() {
const { register, handleSubmit, formState: { errors } } = useForm<FormData>({
resolver: zodResolver(formSchema),
});

const onSubmit = async (data: FormData) => {
console.log("Submitting User form with data:", data);
};
return (
<div className="max-w-2xl space-y-6 p-4 md:p-6">
<div>
<h1 className="text-2xl font-semibold">Add User</h1>
<p className="text-sm text-muted-foreground">Create a new user.</p>
</div>

<form onSubmit={handleSubmit(onSubmit)} className="space-y-4 rounded-lg border p-4 md:p-6">
<div className="space-y-2">
<label htmlFor="name" className="text-sm font-medium">
Name
</label>
<input type="text" id="name" {...register("name")} className="w-full rounded-lg border p-2.5 text-sm text-muted-foreground" />
{errors.name && (
<p className="text-red-500 text-sm">{errors.name.message}</p>
)}
</div>

<div className="space-y-2">
<label htmlFor="email" className="text-sm font-medium">
Email
</label>
<input type="email" id="email" {...register("email")} className="w-full rounded-lg border p-2.5 text-sm text-muted-foreground" />
{errors.email && (
<p className="text-red-500 text-sm">{errors.email.message}</p>
)}
</div>

<div className="space-y-2">
<label htmlFor="username" className="text-sm font-medium">
Username
</label>
<input type="text" id="username" {...register("username")} className="w-full rounded-lg border p-2.5 text-sm text-muted-foreground" />
{errors.username && (
<p className="text-red-500 text-sm">{errors.username.message}</p>
)}

</div>

<div className="space-y-2">
<label htmlFor="password" className="text-sm font-medium">
Password
</label>
<input type="password" id="password" {...register("password")} className="w-full rounded-lg border p-2.5 text-sm text-muted-foreground" />
{errors.password && (
<p className="text-red-500 text-sm">{errors.password.message}</p>
)}
</div>

<div className="space-y-2">
<label htmlFor="confirmPassword" className="text-sm font-medium">
Confirm Password
</label>
<input type="password" id="confirmPassword" {...register("confirmPassword")} className="w-full rounded-lg border p-2.5 text-sm text-muted-foreground" />
{errors.confirmPassword && (
<p className="text-red-500 text-sm">{errors.confirmPassword.message}</p>
)}
</div>

<button type="submit" className="w-full rounded-lg bg-primary px-5 py-2.5 text-sm font-medium text-white">
Add User
</button>

<Link href="/users" className="text-sm text-muted-foreground underline">
Cancel
</Link>
</form>
</div>
);
}




Comments (0)

No comments yet. Be the first to comment!

Latest Articles