Zod email validation
Validate email addresses in Zod 4 with the top-level z.email(), add custom messages, choose a stricter pattern, and migrate from the deprecated z.string().email().
Quick answer: how do you validate an email in Zod 4?
Use the top-level `z.email()`. In Zod 4 the email validator moved from `z.string().email()` to a dedicated `z.email()` schema, which is the recommended form. The old method still works but is deprecated, so prefer `z.email()` in new code.
Basic email validation
Use `z.email()` directly as a field schema. It rejects values that are not valid email addresses and returns the typed string when they are.
import * as z from "zod";
const SignupSchema = z.object({
email: z.email(),
password: z.string().min(8),
});
SignupSchema.parse({ email: "jane@acme.dev", password: "hunter2!" });Custom error messages
Pass an options object (or a string) to set the message shown when validation fails. This is the value you typically render next to the input.
const EmailField = z.email({ error: "Enter a valid email address" });
const result = EmailField.safeParse("not-an-email");
if (!result.success) {
result.error.issues[0].message; // "Enter a valid email address"
}Choose a stricter or unicode pattern
Email validity is famously fuzzy. Zod 4 lets you pass a `pattern` so you can match a specific standard, such as the HTML5 input pattern or a unicode-aware one, instead of the default.
// HTML5 input-style validation
const html5 = z.email({ pattern: z.regexes.html5Email });
// Accept unicode local parts
const unicode = z.email({ pattern: z.regexes.unicodeEmail });Migrate from z.string().email()
If you are upgrading from Zod 3, the chained method is deprecated but still runs. Replace it with the top-level schema as you touch each field; combine with other string rules by chaining on the email schema.
// Before (deprecated in Zod 4) const oldEmail = z.string().email().max(254); // After const newEmail = z.email().max(254);
Email validation in Zod 4
| Approach | What it does | Use when |
|---|---|---|
| z.email() | Top-level email schema in Zod 4 with a sensible default pattern. | New code validating an email field. |
| z.email({ error: "..." }) | Same validation with a custom error message. | You surface the message directly to users. |
| z.email({ pattern: z.regexes.html5Email }) | Swaps in a specific regex such as the HTML5 or unicode pattern. | You need to match a particular standard or accept unicode addresses. |
| z.string().email() | Legacy v3 style, deprecated in Zod 4. | Only in older code you have not migrated yet. |
Use FrameworkKit to generate the starter code, then review the output before shipping it in production.
Generate with the JSON to Zod schema generatorZod validation resources
JSON to Zod converter
Convert sample payloads into copy-ready Zod schemas and inferred TypeScript types in the browser.
JSON to Zod schema examples
Open realistic API and product payload examples before adapting the JSON to Zod schema output.
TypeScript to Zod Converter
Convert TypeScript interfaces and type aliases into Zod schemas when the source shape already lives in code.
Zod to JSON Schema Converter
Use the canonical online converter when an existing Zod schema needs JSON Schema, AJV, or OpenAPI output.
Validate API responses with Zod
Validate API responses, request bodies, and fetch boundaries with Zod schemas in TypeScript.
Zod parse vs safeParse
Choose between parse, safeParse, parseAsync, and safeParseAsync for TypeScript validation flows.
Zod safeParse
Validate unknown data without throwing, narrow the result type, and format safeParse errors.
Zod safeParse error messages
Format safeParse failures, field errors, and API validation responses in TypeScript.
Zod refine vs superRefine
Add custom validation, async checks, and field-level error paths after generating a starter schema.
Zod nativeEnum in Zod 4
Migrate z.nativeEnum patterns to z.enum, validate TypeScript enums, and avoid enum value mistakes.
Use JSON to Zod for form validation
Start from a submitted form payload, then add business rules such as email, length, and enum checks.
Zod to JSON Schema for OpenAPI
Publish Zod API schemas as OpenAPI-compatible contracts when teams need portable documentation.
ZodError flatten vs format
Shape validation errors with z.flattenError() for forms and z.treeifyError() for nested data in Zod 4.
Zod coerce for query params
Turn string query params and form fields into numbers, dates, and booleans, and avoid the coerce.boolean trap.
Zod transform and pipe
Reshape validated data with transform, chain a second check with pipe, and use z.codec for reversible conversions.
Zod vs JSON Schema
Choose between TypeScript-first runtime validation and portable schema contracts.
Zod vs Yup vs Valibot
Compare TypeScript validation libraries for API boundaries, forms, server actions, and bundle tradeoffs.
FAQ
Is z.string().email() deprecated in Zod 4?
Yes. Zod 4 introduces the top-level `z.email()` schema as the recommended form. The chained `z.string().email()` still works but is deprecated, so prefer `z.email()` in new code.
How do I set a custom email error message?
Pass options to the validator, for example `z.email({ error: "Enter a valid email address" })`. The message appears on the failing issue.
Can I use a stricter email regex?
Yes. Pass a `pattern`, such as `z.email({ pattern: z.regexes.html5Email })`, to match a specific standard or accept unicode addresses.
How do I combine email with other rules like max length?
Chain on the email schema, for example `z.email().max(254)`, the same way you would add length or transform rules to any string schema.
Related tools
TypeScript to Zod Converter
Convert TypeScript interfaces and type aliases into Zod schemas with inferred types in your browser.
Zod to JSON Schema Converter
Use a free browser-only online converter to turn Zod 4 schemas into JSON Schema for Draft 2020-12, Draft 7, AJV, or OpenAPI-compatible output.
OpenAPI to Zod
Turn OpenAPI schemas into Zod validators and lightweight typed fetch clients.