Repository navigation
Releases: colinhacks/zod
Release list
v4.6.7
v4.6.6
Six malicious commits pushed to
mainon September 21 with a compromised git credential were reverted the same day in #6620 and have since been removed from the history ofmain. None were ever published to npm. The npm provenance for 4.6.6 names 284243f, the pre-rewrite copy of 9c21983 with identical contents.
Commits:
- 0cd3af5 docs: load the en locale directly in Zod Mini
- 9d5e60f docs: import locales from zod/locales, not z.locales
- 34a626f docs: tighten the z.locales tree-shaking note
- 74304ce release: stage npm versions instead of publishing them (#6622)
- b2ef983 docs: add Code for Japan to bronze sponsors
- 8fb2faf docs: crop the Code for Japan logo and list it first in bronze
- bacad51 docs: square the Code for Japan logo to match the other bronze tiles
- c42596b release: hold the run on the release-approval environment until the staged versions are approved (#6638)
- a2ba4a6 fix(types): declare record output and input as members, not base type arguments (#6645)
- a0ebf97 fix(v4): check array, set and map size bounds before parsing elements (#6652)
- 529f78d 4.6.6
- 9c21983 ci: stage at verbose so a failed OIDC token exchange says why
v4.6.5
Commits:
- d2b135c docs: add the 4.6.x patch highlights to the 4.6 post
- f1448f7 docs: fold the 4.6.x patch highlights into the 4.6 post's own sections
- de65a5c docs: lead the properties section with the check and add a Zod Mini tab (#6598)
- 56222cd feat(instanceof): key the .properties() shape off the instance type (#6600)
- ca0229a Revert "feat: add z.currencyCode() over a vendored ISO 4217 list, refreshed weekly by CI (#6595)"
- cc4cd4e Revert "Revert "feat: add z.currencyCode() over a vendored ISO 4217 list, refreshed weekly by CI (#6595)""
- 0f3f5ee 4.6.5
- 59bbc03 chore: re-pin the integration peers to the workspace zod after the 4.6.5 bump
v4.6.4
A patch on top of 4.6.3.
d6bc1e30feat: addz.currencyCode()over a vendored ISO 4217 list, refreshed weekly by CI (#6595)ad32d751perf:z.url()rejects an invalid URL withURL.canParse()instead of a throwing constructor, about 50x faster; fewer allocations on the validation path (#6588)2bb08717chore: re-pin the integration peers to the workspace zod after the 4.6.4 bumpf6e1701achore(deps): bump next to 15.5.25 and vite to 7.3.6 (#6153)
v4.6.3
A patch on top of 4.6.2.
413cce9afix(v4): make z.properties() a check again (#6594) — removes the standalonez.properties()schema from 4.6.0;z.instanceof().properties()and.check(...z.properties())are unchanged75d63ee1docs: show only the.properties()method form in the 4.6 post46da9572docs: match the error-message examples to what the parsers emit
v4.6.2
v4.6.1
A patch on top of 4.6.0.
b12aa523fix: preserve unique tags with defaulted discriminators (#6582) — closes #6577dd9c36fafix(v4): defer recursive object index inference (#6580)3b154992feat(lang): add Tajik (tg) locale (#6581) by @ismoil772efa8b80ci: give the npm wait a real budget and drop the back-publish path (#6583)
v4.6.0
Zod 4.6 is now available.
npm install zod@latestAt a glance:
.validate()— checks input validity without building a result (up to 35x faster than.safeParse().successon a compiled schema)z.instanceof().properties()— validates properties of an instancefromJSONSchema()— enforces six validation keywords it used to ignorez.iban()— electronic-format IBAN plus mod-97 checksumz.withParser()— installs a parser generated elsewhere, for environments withoutnew Function- Faster CommonJS — drops the getter on every export (~3x faster
z.validate()underrequire) - Memory retention in recursive schemas — releases the parsed input, fixing a 4.5 out-of-memory regression
@zod/mini— Zod Mini as a standalone package, versioned in lockstep withzodsince 4.5
.validate()
Standalone boolean validation, in Zod, Zod Mini, and Zod Core. It answers "is this input valid?" without constructing a ZodError, which makes rejection cheap. The return type is a guard on the schema's input type.
z.validate(z.string(), "hi"); // true
z.validate(z.string(), 42); // falseIt is a method on Zod Classic schemas too. (#6547)
const Player = z.object({
username: z.string(),
xp: z.number(),
});
if (Player.validate(data)) {
data.username; // narrowed
}In conjunction with z.compile(), this can be up to 35x faster than .safeParse().success on invalid input.
Time per call on invalid input, compiled with z.compile() — lower is better (benchmark)
Uncompiled schemas
Without compilation it is up to 5.9x faster. The saving is the result object: .safeParse() allocates one with an accessor pair on every call, and .validate() allocates nothing.
Time per call on invalid input, plain schemas — lower is better (benchmark)
Both charts measure the failure path. The key feature of .validate() is that it can short-circuit on the first issue it encounters, instead of aggregating a full ZodIssue[] array.
Note
Async refinements are covered by .validateAsync().
z.properties()
A new API for validating specific properties of an object. Unlike z.object() it validates in-place, so it plays nice with class instances. (#6536)
const responseLike = z.properties({ status: z.number().min(200).max(299) });
responseLike.parse(new Response("ok", { status: 200 })); // ✅ a real Response
responseLike.parse({ status: 204 }); // ✅ a plain objectA corresponding .properties() method has been added to ZodInstanceOf.
Zod
const okResponse = z.instanceof(Response).properties({
ok: z.literal(true),
status: z.number().min(200).max(299),
});Zod Mini
const okResponse = z.instanceof(Response).check(...z.properties({
ok: z.literal(true),
status: z.number().check(z.minimum(200), z.maximum(299)),
}));The input comes back untouched, so the prototype survives and the methods still work. That is the part z.object() cannot do: it would hand back a plain object and the Response would be gone.
const res = await fetch("/api/user");
okResponse.parse(res) === res; // ✅ truefromJSONSchema()
Six additional JSON Schema keywords are now supported in z.fromJSONSchema(). (#6535)
const schema = z.fromJSONSchema({
type: "object",
minProperties: 2, // also maxProperties
});
schema.parse({ a: 1 }); // ❌ too few properties
schema.parse({ a: 1, b: 2 }); // ✅Both property bounds count the input's own keys. Array uniqueness is structural, so [{ a: 1 }, { a: 1 }] is a duplicate.
z.fromJSONSchema({ type: "array", uniqueItems: true }).parse([{ a: 1 }, { a: 1 }]); // ❌
z.fromJSONSchema({
type: "array",
contains: { type: "number" }, // also minContains and maxContains
minContains: 2,
}).parse(["a", 2]); // ❌ only one numberz.iban()
A new string format: an IBAN in electronic format, with a valid ISO 7064 MOD 97-10 checksum. (#6571)
z.iban().parse("DE89370400440532013000"); // ✅
z.iban().parse("DE89370400440532013001"); // ❌ bad checksumz.withParser()
z.compile() builds its parser with new Function, which a strict Content Security Policy blocks. z.withParser() is that installer on its own: it takes a parser generated somewhere else, at build time or by a native compiler, and installs it under the same contract. (#6575)
const Player = z.object({ username: z.string(), xp: z.number() });
// isPlayer is a type guard your build step generated
const Fast = z.withParser(Player, (input) =>
isPlayer(input) ? { username: input.username, xp: input.xp } : z.INVALID
);The supplied parser owns the whole result, so it has to return what the schema would have returned. This one rebuilds the object rather than handing back its input, because z.object() strips unknown keys. Returning z.INVALID hands the input to the runtime, which stays the only source of ZodErrors.
Faster CommonJS
TypeScript compiles a re-export to a getter, and 252 of the 255 exports on Zod 4.5's CommonJS entrypoint were getters. V8 could not see a constant callee behind one, so it could not inline the call. The 4.6 build emits plain properties and freezes the exports object. On a compiled schema, z.validate() under require is about 3x faster than it was in Zod 4.5. (#6564)
const { z } = require("zod");
const CompiledPlayer = z.compile(Player);
z.validate(CompiledPlayer, data); // ~3x faster than Zod 4.5Only calls through the namespace were affected. A method call like Player.safeParse(data) never reads the exports object, and the ESM build is unchanged.
Memory retention in recursive schemas
A recursive schema held the input and output of its last parse until the next parse replaced it, so one long-lived schema pinned every object it had touched. Zod 4.4 released that input and Zod 4.5 did not, which surfaced as an out-of-memory failure on a repository-wide lint run. The parse state is weak throughout now: one parse of a 29k-node tree retains 2.2 MB where it used to retain 10.1 MB, and recursive parses give up about 6% for it. (#6572)
const Category = z.object({
name: z.string(),
get children() {
return z.array(Category);
},
});Bug fixes
⚠️ Error maps run on the first read of error
Because safeParse() now builds its error lazily, error maps — global, locale, and per-schema error — run when result.error is first read, not at parse time. Code that swaps z.config() between the parse and the read gets the newer configuration. (#6519)
const result = schema.safeParse(12);
z.config(z.locales.fr());
result.error.issues[0].message; // French in 4.6, English in 4.5An error map with a side effect never runs if nothing reads the error. Throwing parses are unaffected — .parse() builds and throws its error immediately, never takes the lazy path, and its stack still points at your call site.
⚠️ z.emoji() rejects component-only strings
Unicode's Emoji_Component property covers the pieces that attach to an emoji, so z.emoji() accepted "123", "#", "*", and a lone zero-width joiner, variation selector, or skin tone modifier. The pattern now requires at least one pictograph, re...
v4.5.4
v4.5.3
Commits:
- e6b6ab3 docs(blog): widen the z.compile example to a 20-property schema
- 87d6464 fix(docs): drop the OG description when the title wraps past two lines
- 99fce39 bench(v4): z.compile() against zod-compiler (#6499)
- e3a695b docs(v4): record the email regex and container output-shape findings under Open
- 7e24a24 docs(blog): drop the reading time and put a GitHub link in the navbar
- eab51ff fix(v4): emit record numeric keys as strings in toJSONSchema (#6497)