--- name: zod description: Zod 4.5 idioms. Use when writing or editing a Zod schema, customizing validation errors, writing transforms or codecs, or converting between Zod and JSON Schema. metadata: version: "0.2.0" --- # Zod 4.5 Training data skews Zod 3, and even Zod 4 knowledge usually stops at 4.0. The installed version is in `package.json`. Each row below names the minor that introduced it, so skip rows above the installed version. ## Write the v4 idiom | Instead of (v3 habit) | Write (v4) | | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | | `z.string().email()` / `.uuid()` / `.url()` | `z.email()`, `z.uuid()`, `z.url()`, `z.hex()`, `z.hash("sha256")`, `z.creditCard()` — top-level | | `z.string().datetime()` | `z.iso.datetime()` (also `z.iso.date()`, `z.iso.time()`, `z.iso.duration()`) | | `message:` / `invalid_type_error` / `required_error` / `errorMap` | one `error:` param — a string or `(issue) => string` | | `.strict()` / `.passthrough()` | `z.strictObject({...})` / `z.looseObject({...})` | | `.merge(other)` | `.extend({...})`; `.safeExtend({...})` when the base has refinements | | `.deepPartial()` method | `z.deepPartial(schema)` (4.5) | | paired one-way `.transform()`s | `z.codec(inSchema, outSchema, { decode, encode })` → `.decode()` / `.encode()` / `.safeDecode()` | | regex `.refine()` for a named format | `z.stringFormat("name", fnOrRegex)` | | `.optional()` when the key may be absent but never `undefined` | `.exactOptional()` (4.3); `.exactPartial()` for whole objects (4.5) | | `z.union` where exactly one branch must match | `z.xor([...])` (4.2) | | hand-written JSON Schema | `z.toJSONSchema(schema)` / `z.fromJSONSchema(json)` (4.2) | | `z.lazy(() => Node)` for a recursive object | a getter on the object: `get children() { return z.array(Node); }` | ## Added after 4.0 - **4.1** codecs (`z.codec`, `z.decode`, `z.encode`), `.safeExtend()`, `z.hash()`, `z.hex()`. - **4.2** `z.fromJSONSchema()`, `z.xor()`, `z.looseRecord(keySchema, valueSchema)` — record that tolerates keys outside the key schema. - **4.3** `.exactOptional()`, `.apply(fn)` for reusable check bundles, `.with(...checks)` alias of `.check()`, `.brand<"Name", "in" | "out" | "inout">()`, type-predicate `.refine((s): s is "a" => ...)` narrowing the output type, `z.string().slugify()`, `z.map().min/max/nonempty()`. - **4.4** `z.invertCodec(codec)`, `.superRefine({ when, ... })` conditional refinement, `ctx.addIssue()` inside `.transform()`. - **4.5** `z.compile(schema)` (3–9x faster parse; or `import "zod/compile"` at the entry point to compile everything), `z.validate(schema, input): boolean` for a cheap validity check, `z.properties({...})` multi-property check for `z.instanceof`, `z.input(schema)` / `z.output(schema)` to parse against one side of a codec, `z.toZod()(schema)` to assert a schema matches a static type, `z.getDiscriminatedOption(union, value)`. ## Facts that bite - **Error precedence is reversed from v3**: a schema-level `error` wins over one passed to `.parse(data, { error })`. - **Refined objects are locked**: `.pick()`, `.omit()`, `.extend()` (overwriting keys) and `.merge()` throw on a schema carrying refinements. Reach for `.safeExtend()`, or refine last. - **`z.iso.datetime()` requires seconds** since 4.5 (RFC 3339); `"2024-01-01T00:00Z"` fails. - **String `.min()/.max()/.length()` count code points** since 4.5, not UTF-16 units. Emoji count as one. - **`z.object({ v: z.undefined() })` requires the key** since 4.4; use `.optional()` for "may be absent". - **Issue types** live under `z.core.$ZodIssue*`; enum and literal mismatches merge into `$ZodIssueInvalidValue`; `$ZodIssueInvalidKey` and `$ZodIssueInvalidElement` cover record, map and set failures. - Unchanged and still expected: `z.infer` for types, `safeParse` at boundaries, discriminated unions, getters for recursive objects. API detail beyond this page: Context7, library ID `/websites/zod_dev`.