zod coerce

Zod coerce for query params and forms

Use z.coerce.number(), z.coerce.boolean(), and z.coerce.date() to turn string query params and form fields into typed values, and avoid the z.coerce.boolean() truthy trap with z.stringbool() in Zod 4.

Open the JSON to Zod converter

Quick answer: how do you turn a string param into a number with Zod?

Wrap the field in `z.coerce.number()`. Query strings, route params, form fields, and env vars all arrive as strings, and coercion runs a JavaScript cast before validation so the parsed output is correctly typed. Use `z.coerce.date()` for dates, but avoid `z.coerce.boolean()` for flags because it treats any non-empty string as true.

Coerce numbers from query strings

A pagination or filter query gives you strings. `z.coerce.number()` casts before validating, so you can still chain `.int()`, `.min()`, and `.max()` to keep the value in range.

import * as z from "zod";

const QuerySchema = z.object({
  page: z.coerce.number().int().min(1).default(1),
  pageSize: z.coerce.number().int().max(100).default(20),
});

QuerySchema.parse({ page: "3", pageSize: "50" });
// { page: 3, pageSize: 50 }

Coerce dates safely

`z.coerce.date()` runs `new Date(value)` and rejects values that produce an Invalid Date. This is convenient for URL filters, but validate the range afterward if only certain dates are allowed.

const RangeSchema = z.object({
  from: z.coerce.date(),
  to: z.coerce.date(),
});

const range = RangeSchema.parse({
  from: "2026-01-01",
  to: "2026-06-30",
});
// { from: Date, to: Date }

The z.coerce.boolean() trap (and z.stringbool fix)

`z.coerce.boolean()` calls `Boolean(value)`. Because any non-empty string is truthy, the string "false" becomes `true`. For real query-string flags, use `z.stringbool()` in Zod 4, which understands "true"/"false", "1"/"0", and "yes"/"no".

// Trap: "false" is a non-empty string -> true
z.coerce.boolean().parse("false"); // true

// Fix in Zod 4
z.stringbool().parse("false"); // false
z.stringbool().parse("yes");   // true

Where coercion belongs

Coercion is for boundaries where data arrives as text. Keep it out of your internal domain models, which should already hold typed values.

  • Use coercion for URL query params, route params, form submissions, env vars, and CSV cells.
  • Do not coerce inside internal types that already hold numbers, dates, or booleans.
  • Combine coercion with `safeParse` so bad params return a structured error instead of throwing.
  • Prefer `z.stringbool()` over `z.coerce.boolean()` for any string flag.

Zod coercion helpers

HelperBehaviorUse when
z.coerce.number()Runs `Number(value)` before validating, so "42" becomes 42 and "" becomes 0.Numeric query params or form fields that arrive as strings.
z.coerce.boolean()Runs `Boolean(value)`, so ANY non-empty string is `true`, including "false".Rarely what you want for query strings; prefer z.stringbool() for real flags.
z.coerce.date()Runs `new Date(value)` and validates the result is a valid date.Date params from URLs, form inputs, or CSV imports.
z.stringbool()Parses "true"/"false", "1"/"0", "yes"/"no" into a real boolean (Zod 4).Boolean flags coming from query strings or env vars.

Use FrameworkKit to generate the starter code, then review the output before shipping it in production.

Generate with the JSON to Zod schema generator

Zod 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 email validation (z.email)

Validate emails with the top-level z.email() in Zod 4, add custom messages, and migrate off z.string().email().

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

How do I convert a string to a number in Zod?

Use `z.coerce.number()`. It runs `Number(value)` before validation, so "42" parses to 42, and you can still chain `.int()`, `.min()`, and `.max()`.

Why does z.coerce.boolean() return true for "false"?

It calls `Boolean(value)`, and any non-empty string is truthy. Use `z.stringbool()` in Zod 4 to parse "true"/"false" and similar strings into the correct boolean.

Does z.coerce.date() handle invalid dates?

Yes. It runs `new Date(value)` and rejects values that produce an Invalid Date. Validate the date range separately if only certain dates are allowed.

Should I coerce values everywhere?

No. Apply coercion only at boundaries where data arrives as strings, such as query params and forms. Internal models should already hold typed values.

Related tools