mcpField
Z
zod

zod

github.com/marcioaltoeDevelopment061 installsUpdated 1d ago
Repository

About zod

Zod is a TypeScript-first schema validation library for developers who need runtime data checks and inferred types. Its API covers parsing, validation, transformations, and JSON Schema conversion, with a Mini variant for tree-shakable use.

Key features

  1. 01Runtime parsing and validation
  2. 02Type inference from schemas
  3. 03Transforms and bidirectional codecs
  4. 04JSON Schema import and export
  5. 05Tree-shakable Zod Mini variant

Use cases

  1. 01Validating input at runtime
  2. 02Deriving TypeScript types from schemas
  3. 03Converting schemas to or from JSON Schema

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/marcioaltoe/skills --skill zod

After installing, type/zodin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

agents/
openai.yaml
references/
advanced.md
codecs.md
json-schema.md
migration.md
zod-mini.md

Details

From the project README

Zod 4

TypeScript-first schema validation. 2kb core bundle (gzipped). Zero dependencies.

CRITICAL: Zod 4 is the current stable version (zod@^4.0.0). Always write Zod 4 code. Never use deprecated Zod 3 patterns.

Import

import * as z from "zod";

For tree-shakable variant (smaller bundles):

import * as z from "zod/mini";

Core Patterns

Parsing
schema.parse(data);           // throws ZodError on failure
schema.safeParse(data);       // returns { success, data?, error? }
await schema.parseAsync(data);
await schema.safeParseAsync(data);
Type Inference
type MyType = z.infer<typeof mySchema>;    // output type
type MyInput = z.input<typeof mySchema>;   // input type
type MyOutput = z.output<typeof mySchema>; // same as z.infer
Primitives
z.string();
z.number();      // finite numbers only (no Infinity, no NaN)
z.bigint();
z.boolean();
z.symbol();
z.undefined();
z.null();
z.date();
z.nan();
Coercion
z.coerce.string();    // String(input)
z.coerce.number();    // Number(input)
z.coerce.boolean();   // Boolean(input)
z.coerce.bigint();    // BigInt(input)
z.coerce.date();      // new Date(input)

String Formats (Top-Level)

Use top-level functions, NOT methods. Methods like z.string().email() are deprecated.

// CORRECT (Zod 4)
z.email();
z.uuid();
z.url();
z.httpUrl();
z.hostname();
z.emoji();
z.base64();
z.base64url();
z.hex();
z.jwt();
z.nanoid();
z.cuid();
z.cuid2();
z.ulid();
z.ipv4();
z.ipv6();
z.mac();
z.cidrv4();
z.cidrv6();
z.hash("sha256");     // "sha1" | "sha384" | "sha512" | "md5"
z.e164();              // phone numbers
z.iso.date();
z.iso.time();
z.iso.datetime();
z.iso.duration();

// DEPRECATED (Zod 3 style — do NOT use)
z.string().email();    // ❌
z.string().uuid();     // ❌
z.string().url();      // ❌
UUID versions
z.uuid();                    // any RFC 9562/4122 UUID
z.uuid({ version: "v4" });  // specific version
z.uuidv4();                  // convenience
z.uuidv6();
z.uuidv7();
z.guid();                    // any 8-4-4-4-12 hex pattern (less strict)
Custom email regex
z.email();                                     // default (Gmail rules)
z.email({ pattern: z.regexes.html5Email });    // browser-style
z.email({ pattern: z.regexes.rfc5322Email });  // RFC 5322
z.email({ pattern: z.regexes.unicodeEmail });  // intl emails
JWTs
z.jwt();
z.jwt({ alg: "HS256" });

Numbers & Integers

z.number();      // any finite number
z.int();         // safe integer range
z.int32();       // int32 range
z.float32();     // float32 range
z.float64();     // float64 range

// bigint variants
z.bigint();
z.int64();
z.uint64();
Number validations
z.number().gt(5);
z.number().gte(5);          // alias: .min(5)
z.number().lt(5);
z.number().lte(5);          // alias: .max(5)
z.number().positive();
z.number().nonnegative();
z.number().negative();
z.number().nonpositive();
z.number().multipleOf(5);   // alias: .step(5)
String validations
z.string().max(5);
z.string().min(5);
z.string().length(5);
z.string().regex(/pattern/);
z.string().startsWith("abc");
z.string().endsWith("xyz");
z.string().includes("---");
z.string().uppercase();
z.string().lowercase();
z.string().trim();
z.string().toLowerCase();
z.string().toUpperCase();
z.string().normalize();

Objects

z.object({ name: z.string(), age: z.number() });          // strips unknown keys
z.strictObject({ name: z.string() });                      // errors on unknown keys
z.looseObject({ name: z.string() });                       // passes unknown keys through

Deprecated: .strict(), .passthrough(), .strip(), .merge(), .deepPartial().

Object methods
schema.extend({ newField: z.string() });    // add fields
schema.pick({ name: true });                // pick fields
schema.omit({ age: true });                 // omit fields
schema.partial();                           // all optional
schema.partial({ name: true });             // some optional
schema.required();                          // all required
schema.keyof();                             // ZodEnum from keys
schema.shape.name;                          // access inner schema

Prefer spread syntax for best tsc performance:

z.object({ ...Base.shape, ...Extra.shape, newField: z.string() });
Recursive objects
const Category = z.object({
  name: z.string(),
  get subcategories() {
    return z.array(Category);
  },
});

Enums

z.enum(["A", "B", "C"]);               // string enum
z.enum(MyTSEnum);                       // TypeScript enum (replaces z.nativeEnum())
z.enum({ A: 0, B: 1 } as const);       // enum-like object

Deprecated: z.nativeEnum(). Use z.enum() instead.

Arrays, Tuples, Sets, Maps

z.array(z.string());
z.array(z.string()).min(1).max(10).length(5);
z.tuple([z.string(), z.number()]);
z.tuple([z.string()], z.number());    // variadic: [string, ...number[]]
z.set(z.number());
z.map(z.string(), z.number());

Unions & Intersections

z.union([z.string(), z.number()]);
z.discriminatedUnion("status", [
  z.object({ status: z.literal("ok"), data: z.string() }),
  z.object({ status: z.literal("err"), error: z.string() }),
]);
z.xor([z.string(), z.number()]);          // exclusive union (exactly one match)
z.intersection(schemaA, schemaB);

Records

z.record(z.string(), z.number());                    // Record<string, number>
z.record(z.enum(["a", "b"]), z.string());            // exhaustive: { a: string; b: string }
z.partialRecord(z.enum(["a", "b"]), z.string());     // partial: { a?: string; b?: string }
z.looseRecord(z.string().regex(/^x_/), z.number());  // pass through non-matching keys

Breaking: z.record(z.string()) single-arg form is removed. Always pass both key and value schemas.

Literals

z.literal("hello");
z.literal(42);
z.literal(true);
z.literal(["a", "b", "c"]);   // multi-value literal (new in Zod 4)

Files

z.file();
z.file().min(10_000);                          // min size in bytes
z.file().max(1_000_000);                       // max size in bytes
z.file().mime("image/png");                    // single MIME
z.file().mime(["image/png", "image/jpeg"]);    // multiple MIMEs

Stringbool

z.stringbool();    // "true"/"yes"/"1"/"on"/"y"/"enabled" → true, inverses → false
z.stringbool({ truthy: ["yes", "y"], falsy: ["no", "n"] });
z.stringbool({ case: "sensitive" });

Template Literals

z.templateLiteral(["hello, ", z.string()]);                    // `hello, ${string}`
z.templateLiteral([z.number(), z.enum(["px", "em", "rem"])]);  // `${number}px` | ...

Optionals, Nullables, Defaults

z.optional(z.string());          // or z.string().optional()
z.nullable(z.string());          // or z.string().nullable()
z.nullish(z.string());           // optional + nullable

z.string().default("hello");     // short-circuits on undefined, returns output type
z.string().prefault("hello");    // pre-parse default, runs through validation
z.number().catch(42);            // fallback on any validation error

Breaking: .default() now expects output type, not input type. Use .prefault() for old behavior.

Transforms & Pipes

z.transform((val) => String(val));                      // standalone transform
z.string().transform((val) => val.length);              // pipe string → transform
z.preprocess((val) => String(val), z.string());         // transform → pipe to schema
z.string().pipe(z.transform((val) => val.length));      // explicit pipe

// .overwrite() — transform without changing inferred type
z.number().overwrite((val) => val ** 2);

Codecs (Bidirectional Transforms)

New in Zod 4.1. Define encode/decode pairs:

const stringToDate = z.codec(z.iso.datetime(), z.date(), {
  decode: (s) => new Date(s),
  encode: (d) => d.toISOString(),
});

stringToDate.decode("2024-01-15T10:30:00.000Z");  // → Date
stringToDate.encode(new Date());                    // → string
stringToDate.parse("2024-01-15T10:30:00.000Z");    // → Date (same as decode at runtime)

Error Customization

Use unified error param (replaces message, errorMap, invalid_type_error, required_error):

z.string().min(5, { error: "Too short" });
z.string({ error: (issue) => issue.input === undefined ? "Required" : "Not a string" });
z.string({ error: (issue) => {
  if (issue.code === "too_small") return `Min ${issue.minimum}`;
}});

Deprecated: message, errorMap, invalid_type_error, required_error.

Metadata & Registries

z.string().meta({ id: "email", title: "Email", description: "User email" });
z.string().describe("A description");      // shorthand for .meta({ description: ... })

// Custom registries
const myReg = z.registry<{ description: string }>();
z.string().register(myReg, { description: "..." });

JSON Schema

z.toJSONSchema(schema);                                        // Zod → JSON Schema
z.toJSONSchema(schema, { target: "draft-07" });                // specific draft
z.toJSONSchema(schema, { target: "openapi-3.0" });             // OpenAPI 3.0
z.fromJSONSchema(jsonSchema);                                  // JSON Schema → Zod (experimental)

Refinements

z.string().refine((val) => val.includes("@"), { error: "Must contain @" });
z.string().refine((val) => val.includes("@"), { error: "...", abort: true }); // stop on failure
z.array(z.string()).superRefine((val, ctx) => {
  ctx.addIssue({ code: "custom", message: "...", input: val });
});

Refinements now live inside schemas (not ZodEffects wrapper). You can interleave .refine() with other methods:

z.string().refine(v => v.includes("@")).min(5);  // works in Zod 4!

Error Pretty-Printing

z.prettifyError(zodError);   // formatted multi-line string
z.treeifyError(zodError);    // tree structure (replaces .format() and .flatten())

Further Reference

  • Zod Mini: See references/zod-mini.md for tree-shakable API differences, .check() usage, and bundle size tradeoffs
  • Codecs: See references/codecs.md for bidirectional transforms, encoding behavior, and common codec patterns (stringToDate, jsonCodec, etc.)
  • JSON Schema: See references/json-schema.md for z.toJSONSchema() options, format conversion, registry-based multi-schema, and z.fromJSONSchema()
  • Advanced patterns: See references/advanced.md for registries, refinement when, .superRefine(), .check(), functions, branded types, readonly, custom schemas, and advanced string/object/record/union options
  • Migration from Zod 3: See references/migration.md for all breaking changes and deprecated APIs

Comments

Markdown supported