The first minor version since the introduction of Zod 4 back in May. This version contains a number of features that barely missed the cut for the 4.0 release. With Zod 4 stable and widely adopted, there's more time to resume feature development.
Codecs
This is the flagship feature of this release. Codecs are a new API & schema type that encapsulates a bi-directional transformation. It's a huge missing piece in Zod that's finally filled, and it unlocks some totally new ways to use Zod.
const stringToDate = z.codec(
z.iso.datetime(), // input schema: ISO date string
z.date(), // output schema: Date object
{
decode: (isoString) => new Date(isoString),
encode: (date) => date.toISOString(),
}
);
New top-level functions are added for processing inputs in the forward direction ("decoding") and backward direction ("encoding").
stringToDate.decode("2025-08-21T20:59:45.500Z")
// => Date
stringToDate.encode(new Date())
// => "2025-08-21T20:59:45.500Z"
Note — For bundle size reasons, these new methods have not added to Zod Mini schemas. Instead, this functionality is available via equivalent top-level functions.
// equivalent at runtime z.decode(stringToDate, "2024-01-15T10:30:00.000Z"); z.encode(stringToDate, new Date());
.parse() vs .decode()
Both .parse() and decode() process data in the "forward" direction. They behave identically at runtime.
stringToDate.parse("2025-08-21T20:59:45.500Z");
stringToDate.decode("2025-08-21T20:59:45.500Z");
There is an important difference however. While .parse() accepts any input, .decode() expects a strongly typed input. That is, it expects an input of type string, whereas .parse() accepts unknown.
stringToDate.parse(Symbol('not-a-string'));
// => fails at runtime, but no TypeScript error
stringToDate.decode(Symbol("not-a-string"));
// ^ ❌ Argument of type 'symbol' is not assignable to parameter of type 'Date'. ts(2345)
This is a highly requested feature unto itself: