What's new in Zod 4?

What's new in Zod 4 image

Zod 4 in now in beta after about a year of development from the team. As a lot of libraries and toolset have been doing recently, Zod has been on a diet. Now it's slimmer, faster and it's been working on efficiency with TypeScript.

It was a very long time ago that Zod came out with their last major update, Zod 3. Back in 2021 we saw ver. 3 released and that's also when the uptake with Zod was building. The GitHub repo had 2,700 stars, now it has 36,500 stars. Weekly downloads were at 600,000 in 2021 and now they are at 23 million.

And as with most popular libraries, Zod is an open-source, community driven library which is written for the love of tools and libraries for all, but thankfully this one has the financial support from Clerk, one of many authentication providers that start with a free plan and scale up. We all like something for free so check out Clerk if you're looking for something like that.

Enhanced type inference capabilities

Zod 4 introduces a new API for defining object types. The z.inteface() seems a little confusing because why should you need a interface when we already have z.object and also are they trying to align more with TypeScript which also has object and interface?

Well it's a little bit similar because in TypeScript a property can be optional in two ways:

type KeyOptional = { prop?: string };
type ValueOptional = { prop: string | undefined };

That means in the KeyOptional you can mark the prop as optional and in the ValueOptional you can mark the prop valve as optional.

Number formats

Number formatting has always been a pain to get right. With Zod 4 it's now got built in support for all the day to day formats we use in TypeScript. Anything else we can do with a custom format.

z.int();      // [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],
z.float32();  // [-3.4028234663852886e38, 3.4028234663852886e38]
z.float64();  // [-1.7976931348623157e308, 1.7976931348623157e308]
z.int32();    // [-2147483648, 2147483647]
z.uint32();   // [0, 4294967295]
z.int64();    // [-9223372036854775808n, 9223372036854775807n]
z.uint64();   // [0n, 18446744073709551615n]

Improved error handling and customisation

One of the most frustrating things a user can experience is a validation error on a site with poor error handling. Zod had the zod-validation-error package to use with Zod 3 but now Zod 4 has that all built in and it's been improved to have prettification on top:

const error = new z.ZodError([
  {
    code: 'unrecognized_keys',
    keys: [ 'extraField' ],
    path: [],
    message: 'Unrecognized key: "extraField"'
  },
  {
    expected: 'string',
    code: 'invalid_type',
    path: [ 'username' ],
    message: 'Invalid input: expected string, received number'
  },
  {
    origin: 'number',
    code: 'too_small',
    minimum: 0,
    inclusive: true,
    path: [ 'favoriteNumbers', 1 ],
    message: 'Too small: expected number to be >=0'
  }
]);

z.prettifyError(error);

This will return the following:

  ✖ Unrecognized key: "extraField"
  ✖ Invalid input: expected string, received number
    → at username
  ✖ Invalid input: expected number, received string
    → at favoriteNumbers[1]

Here's to hoping that we'll be able to configure this in the future, but for now we're stuck with it like this!

Better performance optimisations

Performance has been a big focus for the latest version of Zod. Benchmarking the basics shows a vast improvement. With the object being the most heavily used and parsed, having a 10x faster version with 4 is going to make the runtimes tumble.

benchmark      time (avg)             (min … max)      p75      p99      p999
-----------------------------------------------------------------------------
• z.object().parse
-----------------------------------------------------------------------------
zod3          857 µs/iter     (604 µs … 4'106 µs)    668 µs  3'782 µs  4'106 µs
zod4       61'999 ns/iter  (44'043 ns … 2'156 µs) 51'728 ns    215 µs  1'680 µs
valibot       351 µs/iter       (301 µs … 973 µs)    345 µs    626 µs    742 µs

summary for z.object().parse
  zod4
  5.66x faster than valibot
  13.83x faster than zod3

Custom email regex

When developers want to regex they'll more than likely look up online for a regex that they think is going to solve all their problems. But without knowledge of what that regex pattern is doing, and some lengthy testing, there will usually be some edge cases that are going to fall fowl of the requirements. With Zod 4 we can now select from common email expressions, as well as defining our own ones:

// Zod's default email regex (Gmail rules)
z.email(); // z.regexes.email
 
// the regex used by browsers to validate input[type=email] fields
z.email({ pattern: z.regexes.html5Email });
 
// the classic emailregex.com regex (RFC 5322)
z.email({ pattern: z.regexes.rfc5322Email });
 
// a loose regex that allows Unicode (good for intl emails)
z.email({ pattern: z.regexes.unicodeEmail });

This is going to help quickly create a verification pattern for emails, and should cover what we usually want to use. But for more complex requirements we can also have some custom patterns using standard string.

Summary

Well that's a few really good additions to Zod and with it's popularity growing, and this still being a Beta release of Zod 4 we can expect to see some more features being added as work continues. There are some other things that I've not covered here, but it's a very big update and I'll come back and have a look again when we get to full release. But for now I recommend you check out Zod and give it a try. It works well with Next.js too so it's something I would definitely recommend to have a look at.

All articles are my own thoughts. Gathered from reading articles and trying things out.