Skip to main content

Next Js Form Validation

· 4 min read
Sivabharathy

Form validation is crucial for ensuring data integrity and a smooth user experience. In Next.js, we can validate forms using various approaches. This guide explores two methods: React Hook Form with Zod (Recommended) and Manual Validation. Both approaches include real-world examples with proper error message handling.


Why use React Hook Form?

✅ Lightweight and optimized for performance
✅ Handles form validation efficiently
✅ Easily integrates with schema validation libraries like Zod

Installation

To get started, install the required dependencies:

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

Implementation

Below is a Next.js form component using react-hook-form and zod for validation:

"use client";

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

// Define schema using Zod
const schema = z.object({
name: z.string().min(3, "Name must be at least 3 characters"),
email: z.string().email("Invalid email address"),
password: z.string().min(6, "Password must be at least 6 characters"),
});

export default function RegisterForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: zodResolver(schema),
});

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

return (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
{/* Name Field */}
<div>
<label className="block">Name</label>
<input {...register("name")} className="border p-2 w-full" />
{errors.name && <p className="text-red-500">{errors.name.message}</p>}
</div>

{/* Email Field */}
<div>
<label className="block">Email</label>
<input {...register("email")} className="border p-2 w-full" />
{errors.email && <p className="text-red-500">{errors.email.message}</p>}
</div>

{/* Password Field */}
<div>
<label className="block">Password</label>
<input
type="password"
{...register("password")}
className="border p-2 w-full"
/>
{errors.password && (
<p className="text-red-500">{errors.password.message}</p>
)}
</div>

<button type="submit" className="bg-blue-500 text-white p-2 rounded">
Submit
</button>
</form>
);
}

How It Works

  • useForm initializes the form and provides methods for handling validation.
  • register("fieldName") connects input fields to the form.
  • zodResolver(schema) integrates Zod for schema-based validation.
  • Error messages are displayed conditionally using {errors.field?.message}.

2️⃣ Using Built-in Validation in Next.js (Manual Approach)

For smaller projects, manual validation can be a good alternative.

Implementation

"use client";

import { useState } from "react";

export default function BasicForm() {
const [formData, setFormData] = useState({
name: "",
email: "",
password: "",
});
const [errors, setErrors] = useState({ name: "", email: "", password: "" });

const validate = () => {
let newErrors = { name: "", email: "", password: "" };
if (formData.name.length < 3)
newErrors.name = "Name must be at least 3 characters";
if (!formData.email.includes("@"))
newErrors.email = "Invalid email address";
if (formData.password.length < 6)
newErrors.password = "Password must be at least 6 characters";

setErrors(newErrors);
return Object.values(newErrors).every((error) => error === "");
};

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (validate()) {
console.log("Form Submitted", formData);
}
};

return (
<form onSubmit={handleSubmit} className="space-y-4">
{/* Name Field */}
<div>
<label className="block">Name</label>
<input
type="text"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
className="border p-2 w-full"
/>
{errors.name && <p className="text-red-500">{errors.name}</p>}
</div>

{/* Email Field */}
<div>
<label className="block">Email</label>
<input
type="text"
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
className="border p-2 w-full"
/>
{errors.email && <p className="text-red-500">{errors.email}</p>}
</div>

{/* Password Field */}
<div>
<label className="block">Password</label>
<input
type="password"
value={formData.password}
onChange={(e) =>
setFormData({ ...formData, password: e.target.value })
}
className="border p-2 w-full"
/>
{errors.password && <p className="text-red-500">{errors.password}</p>}
</div>

<button type="submit" className="bg-blue-500 text-white p-2 rounded">
Submit
</button>
</form>
);
}

How It Works

  • Uses useState to manage form data and errors.
  • The validate function checks each field manually.
  • Errors are updated in state and displayed conditionally.

✅ Summary of Approaches

ApproachProsCons
React Hook Form + Zod✅ Cleaner Code, ✅ Auto-Validation, ✅ Better Performance❌ Requires dependencies
Manual Validation✅ No Dependencies, ✅ More Control❌ More Code, ❌ Repetitive Logic

🚀 For larger projects, use React Hook Form + Zod.
For small projects, manual validation works fine.

Need help integrating form submission with a Next.js API? Let me know! 🎯