Zod Default: Set Default Values in Zod Schemas


Looking to prevent unexpected undefined values and make your Zod schemas more predictable? Zod’s default() method is your secret weapon.

The default() ensures your fields always have a safe fallback — without forcing your users to provide every value.

This guide walks you through how defaults actually work under the hood, shows real usage patterns, and explains common mistakes developers make when mixing defaults with transforms, optional values, and complex types.

Note: We used Zod v4 to run and test all the default() code examples in this guide, ensuring everything works exactly as shown.

How Zod Handles Defaults on null and Validation Errors

Zod’s default() method only triggers when the input value is undefined. This is the key rule that decides whether your default runs or gets ignored.

Because of this, developers often expect defaults to apply when a value is null or when validation fails, but Zod doesn’t behave that way out of the box.

Default on null

If a field receives null, Zod does not treat it as “missing.”

null goes through validation like any other value, and if the schema doesn’t allow null, the result is a validation error — not a fallback to the default.

z.string().default("hello")

To allow null but still use a default, you must explicitly convert it to undefined or transform it:

z.string().nullable().transform(val => val ?? undefined).default("hello");

Default on Validation Error

Zod does not auto-fallback to your default when a value is invalid.

If the value is wrong — wrong type, wrong format, wrong shape — Zod will throw an error even when a default exists. So use prefault() method.


Zod default vs prefault

  • default()
    • Runs only when the input is undefined.
    • Does not run for null or invalid values.
    • Does not override validation errors.
    • Applied at the end of parsing after checking if value is missing.
    • z.string().default("hi").parse(undefined)
  • prefault()
    • Runs before validation.
    • Converts any invalid input into the fallback before schema rules apply.
    • Useful for pre-cleaning bad input or replacing null, empty strings, or bad types.
    • z.string().prefault("hi").parse(null);

Zod default vs catch

  • default()
    • Only triggers when input is undefined.
    • Cannot recover from validation failures.
  • catch()
    • Triggers only when validation fails.
    • Great for fallback-on-error behavior.
    • z.number().catch(10).parse("oops")

Zod default vs optional

  • default()
    • Converts undefined → fallback value.
    • Field becomes implicitly optional if a default is provided.
    • Still validates when a user provides a value.
  • optional()
    • Allows undefined as a valid input.
    • Does not apply any fallback.
    • The output may still be undefined.
    • z.string().optional().parse(undefined)

Using Zod Defaults Across Different Data Types

Here’s how each Zod datatype behaves with default() and how you’d actually use them in real projects.

String Default

Setting a default username when a user signs up without providing one.

const UsernameSchema = z.string().default("guest");

Number Default

Auto-set the page number in pagination APIs. Parse integer 1 as default.

const PageSchema = z.number().int().default(1);

Boolean Default

Enable/disable email notifications by default in a profile form. Can default to true or false.

const EmailNotifySchema = z.boolean().default(true);

Array Default

Ensure an API always receives an array of tags, even if the client sends nothing. Default to empty array.

const TagsSchema = z.array(z.string()).default([]);

Object Default

Fallback user preferences when the client doesn’t send them. Default to empty object.

const PrefsSchema = z.object({
  theme: z.string().default("light"),
  fontSize: z.number().default(14),
}).default({});

Enum Default

Assign a default user role during registration.

const RoleSchema = z.enum(["user", "admin"]).default("user");

Literal Default

Versioning API requests where a default version should always exist.

const VersionSchema = z.literal("v1").default("v1");

Date Default

Assign the current timestamp when log entries are created.

const LogDateSchema = z.date().default(() => new Date());

Conditional Default Value

Sometimes you need a default value that depends on other fields or depends on the incoming value itself. Zod doesn’t have a built-in “conditional default” method, but you can achieve it using transform, superRefine, or default + transform combos.

Example: If a string is empty, assign a fallback.

z.string()
  .transform(val => val.trim() === "" ? "guest" : val)
  .default("guest");

Dynamic Default Value

A dynamic default means the value is calculated at parse time, not hardcoded.
Zod achieves this by passing a function into .default(() => … ).

This is perfect when your default depends on:

  • the current date
  • random IDs
  • an environment value

Example: Dynamic default ID (random / UUID)

const UserSchema = z.object({
  id: z.string().default(() => randomUUID()),
  username: z.string(),
});

Summary

Zod’s default() gives your schemas safe fallback values, but it only runs when the input is undefined. It does not apply defaults for null or invalid values — those will still throw validation errors unless you explicitly handle them. That’s where prefault() (fallback before validation) and catch() (fallback on validation error) come in.

Key takeaways:

  • default() → triggers only on undefined; doesn’t fix invalid input.
  • prefault() → replaces bad input before validation; great for cleaning nulls, empty strings, or incorrect types.
  • catch() → applies when validation fails; perfect for error-based fallbacks.
  • optional() → allows undefined but doesn’t supply a fallback.

Defaults behave consistently across all data types — strings, numbers, booleans, arrays, objects, enums, literals, and dates — and you can use transforms to emulate conditional or dynamic defaults when needed. Passing a function into .default() lets you generate values at parse time (e.g., timestamps, UUIDs).

In short: Zod defaults make schemas more predictable, but understanding when they trigger (and when they don’t) is essential for avoiding unexpected undefined values and building safer data workflows.

Author

Sharukhan is the founder of Tecktol. He has worked as a software engineer specializing in full-stack web development.