Home
Next.js
Next.js: Zod Validation
Daniel Nguyen
Daniel Nguyen
October 18, 2026
1 min

TypeScript checks types during development, but it cannot validate data at runtime. For example, an API might return unexpected data, or a customer might submit an invalid phone number.

Zod is a TypeScript-first validation library that checks whether data matches a schema at runtime. It can also transform data and infer TypeScript types directly from schemas.

1. Define a Schema

A schema describes the structure and validation rules of your data.

import { z } from "zod";
const checkoutSchema = z.object({
name: z.string().trim().min(2, "Name is too short"),
phone: z.string().regex(
/^0\d{9}$/,
"Invalid phone number"
),
age: z.number().min(18),
});
type CheckoutForm = z.infer<typeof checkoutSchema>;

Here, z.object() defines an object schema, while z.string() and z.number() define the expected data types.

Common validation methods include:

  • .min() and .max() — Set minimum and maximum values or lengths.
  • .regex() — Validate a string against a regular expression.
  • .optional() — Allow a value to be undefined.
  • .nullable() — Allow a value to be null.

z.infer<typeof checkoutSchema> automatically derives the TypeScript type from the schema, avoiding duplicate type definitions.

2. Validate Data with parse() and safeParse()

Zod provides two main methods for validating data.

parse()

Use parse() when invalid data should throw a ZodError.

const data = checkoutSchema.parse(formValues);

If the data is invalid, execution throws an error. Otherwise, data contains the validated output.

safeParse()

Use safeParse() when you want to handle validation errors explicitly.

const result = checkoutSchema.safeParse(formValues);
if (!result.success) {
console.log(result.error.issues);
return;
}
await createOrder(result.data);

safeParse() returns a result object with either success: true and validated data, or success: false and a ZodError.

For form validation, safeParse() is often convenient because invalid input can be handled without throwing an exception.

3. Transform and Normalize Data

Validation is not always just about checking data. Sometimes, input needs to be cleaned before it is used.

`.trim()

Removes whitespace from the beginning and end of a string.

z.string().trim().parse(" Viet ");
// "Viet"

.transform()

Converts validated input into a different output.

const phoneSchema = z.string()
.transform((value) => value.replace(/\s/g, ""))
.pipe(z.string().regex(/^0\d{9}$/));
phoneSchema.parse("038 656 9374");
// "0386569374"

4. Add Custom Validation with refine()

Use .refine() when built-in validators are not enough.

const passwordSchema = z.string().refine(
(value) => value.length >= 8,
{ error: "Password must be at least 8 characters" }
);

For more complex validation, .superRefine() can report multiple issues and associate them with specific fields.

For example, it can validate that a password and its confirmation match.


Tags

#NextJS#Zod

Share

Daniel Nguyen

Daniel Nguyen

Frontend Developer

Frontend developer specializing in React, Next.js, and JavaScript. Writing practical guides on modern web development at Dev98.

Expertise

React
Next.js
JavaScript
TypeScript
Python

Social Media

githublinkedinyoutubewebsite

Related Posts

Next.js
Supabase Features
October 29, 2026
3 min
Dev98

Dev98

React · Next.js · Web development