mrkeyoor.com_
Sat 19 Sept 06:43 UTC
npmUtilsupdated 19 Sept 2026

zod review

Zod 4.4.3 defines runtime schemas in TypeScript and derives static input and output types from them. A schema parses unknown data into a cloned value or returns structured issues with paths and error codes. Version 4 includes objects, unions, coercion, recursive schemas, refinements, transforms, bidirectional codecs, metadata registries, and JSON Schema conversion. Release 4.4.3 restores `catch` and `preprocess` behavior for absent object properties after regressions in earlier 4.4 patches. Zod checks only the values passed to a parse method; it does not validate every network response or replace ordinary TypeScript types inside trusted code.

264.4Mdownloads / wk
Verdict

Zod 4.4.3 installed in 0.7 seconds with 0 dependencies and 0 audit findings, but our full import still measured 63.2 KB gzipped. It fits TypeScript boundaries that need runtime checks and inferred types; set unknown-key and transform semantics explicitly, and measure real client chunks before making it the browser default.

We installed it

Lab card: what happened when we installed zodScreenshot of zod documentation
Install✓ · 0.7s6 packages on disk · 7 MB
ImportESM import works · require() works · ESM package with exports map
Browser63.2 KBgzipped (323.3 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does zod install cleanly?

Yes. In a fresh container with an empty cache, npm install zod finished in 0.7s, leaving 6 packages and 7 MB on disk. npm audit reported no known vulnerabilities.

How much does zod add to a browser bundle?

63.2 KB gzipped (323.3 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does zod work with both ESM and CommonJS?

Yes. Both import 'zod' and require('zod') worked in Node 22 in our run. The package is published as ESM with an exports map.

Does zod include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

zod or valibot: which should you use?

valibot: Use it for modular browser validation where retained code size is the first constraint. Zod 4.4.3 installed in 0.7 seconds with 0 dependencies and 0 audit findings, but our full import still measured 63.2 KB gzipped.

When should you not use zod?

Every value is created inside trusted, typed code and no runtime boundary exists. Plain TypeScript types add no parser or bundle code.

API stability4/5Zod 4 has a defined current API and keeps a v3 compatibility subpath, while patches remain within the same schema model. The v3 migration changed formats, errors, records, defaults, and advanced types. Version 4.4.3 also repairs regressions for absent-key `catch` and `preprocess`. Boundary tests should cover missing, null, malformed, transformed, and extra-key inputs rather than only valid examples.
Docs5/5zod.dev documents parsing, error issues, every schema family, object modes, unions, refinements, transforms, codecs, metadata, registries, JSON Schema, packages, and the v3 migration. Examples pair runtime calls with inferred TypeScript types. The surface is large, so defaults such as unknown-key stripping and coercion still deserve a local convention near application schemas instead of relying on developers to remember one documentation page.
Maintenance4/5GitHub shows a push on 2026-08-26, 43,530 stars, 79 open issues and pull requests in its combined counter, and an unarchived repository. Version 4.4.3 shipped on 2026-05-04 with two runtime corrections for missing object keys. Work is active, although wide adoption creates compatibility pressure across form libraries, routers, generators, and other packages that may support different Zod majors.
Ecosystem5/5npm counted 272,031,420 downloads from 2026-08-19 through 2026-08-25. Zod schemas are accepted by form resolvers, RPC systems, routers, OpenAPI bridges, AI SDKs, environment loaders, and backend frameworks. Built-in JSON Schema conversion extends that reach. Integrations can still lose refinements or transform semantics when they understand only part of Zod's model.

Use it if

  • Unknown JSON, form fields, environment values, webhooks, or model output must become a checked TypeScript value.
  • One definition should provide runtime validation, inferred input and output types, and a JSON Schema where semantics permit.
  • A router, form resolver, RPC layer, OpenAPI bridge, or structured-output SDK already accepts Zod schemas.
  • Readable nested issues and custom business rules matter more than the smallest possible client validator.
Skip it if

Setup reality

We installed Zod 4.4.3 in 0.7 seconds in a fresh Node 22 container. The result was 6 packages and 7 MB on disk. Zod itself declares 0 direct dependencies and 0 peers, is 6,524 KB unpacked, includes TypeScript declarations, and uses the MIT license. npm audit reported 0 known vulnerabilities. The package is ESM with an exports map, and both require() and ESM import worked.

Our full-package esbuild probe measured 323.3 KB minified and 63.2 KB gzipped. That test imported the entire namespace, so it is an upper-bound integration result rather than the cost of one object schema. Named imports can tree-shake, and Zod Mini provides a smaller functional API. Measure the actual client chunk because locales, error helpers, schema composition, and namespace imports change what the bundler retains.

Use TypeScript strict mode and keep one Zod major across adapters. The move from v3 to v4 changed string-format helpers, error customization, records, defaults, and several advanced schemas. An integration typed only for v3 may require a compatibility subpath or duplicate versions. Release 4.4.3 fixes missing-property behavior for catch and preprocess, so tests should include absent keys alongside null, malformed, and valid values.

Set an object policy at each external boundary. z.object strips unknown keys, z.strictObject rejects them, and z.looseObject retains them. parse() throws, while safeParse() returns a discriminated result. Async refinements require parseAsync() or safeParseAsync() at the caller. Transforms and codecs have distinct input and output types, and some refinements cannot be expressed in generated JSON Schema. Build reusable schemas once instead of reconstructing them inside hot request loops.

Patterns

Validate an unknown object parse-object

import * as z from 'zod';

const User = z.object({
  id: z.uuid(),
  name: z.string().min(1),
  age: z.number().int().nonnegative(),
});

const user = User.parse(input);

`parse()` throws `ZodError`, returns a clone, and strips unrecognized object keys under plain `z.object`.

Handle invalid input without an exception safe-parse

const result = User.safeParse(input);
if (!result.success) {
  return { status: 400, issues: result.error.issues };
}

const user = result.data;

The `success` field narrows the result; do not echo user-supplied values from issue data without review.

Separate types around a transform infer-input-output

const Port = z.string()
  .transform((value) => Number.parseInt(value, 10))
  .pipe(z.number().int().min(1).max(65535));

type PortInput = z.input<typeof Port>;
type PortOutput = z.output<typeof Port>;

`z.infer` equals the output type; adapters and forms commonly operate on the pre-transform input type.

Reject unknown object properties reject-extra-keys

const Payload = z.strictObject({
  action: z.literal('create'),
  name: z.string(),
});

Use `z.looseObject` to retain extra keys or `z.object` to strip them; make this choice visible at external boundaries.

Parse environment strings parse-environment

const Env = z.object({
  PORT: z.coerce.number().int().min(1).max(65535).default(3000),
  DEBUG: z.stringbool().default(false),
});

const env = Env.parse(process.env);

`z.coerce.boolean` treats any nonempty string as true; `z.stringbool` understands textual true and false values.

Report a cross-field error on one path validate-related-fields

const Signup = z.object({
  password: z.string().min(12),
  confirmation: z.string(),
}).refine((data) => data.password === data.confirmation, {
  path: ['confirmation'],
  error: 'Passwords do not match',
});

An async refinement requires `parseAsync()` or `safeParseAsync()`; synchronous parsing cannot run it.

Dispatch a union by a literal field parse-tagged-union

const Event = z.discriminatedUnion('type', [
  z.object({ type: z.literal('click'), x: z.number(), y: z.number() }),
  z.object({ type: z.literal('key'), key: z.string() }),
]);

Each member must expose a compatible literal discriminator; tagged dispatch usually gives clearer failures than an untagged union.

Validate a recursive tree define-recursive-schema

const Category = z.object({
  name: z.string(),
  get children() {
    return z.array(Category);
  },
});

type Category = z.infer<typeof Category>;

A schema does not impose a depth limit; cap hostile recursive input before it can exhaust stack or memory.

Flatten issues for a form format-field-errors

const result = Signup.safeParse(values);
if (!result.success) {
  const errors = z.flattenError(result.error);
  console.log(errors.fieldErrors.confirmation);
}

Flattening is convenient for shallow forms; retain original issue paths for nested arrays and objects.

Convert a schema to JSON Schema emit-json-schema

const schema = z.toJSONSchema(User, {
  target: 'draft-2020-12',
});

Custom refinements and transforms may have no JSON Schema equivalent, so test the generated contract downstream.

Decode and encode an ISO date define-codec

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

const date = z.decode(IsoDate, input);
const text = z.encode(IsoDate, date);

A codec has different input and output types and supports both directions; a transform is one-way.

Return a fallback after validation fails supply-fallback

const DisplayName = z.string().min(1).catch('Anonymous');
const name = DisplayName.parse(input);

`catch()` converts bad input into success and can hide upstream data errors; 4.4.3 restores its absent-key handling.

Alternatives

PackageRegistryPick it when
valibotnpmUse it for modular browser validation where retained code size is the first constraint.
ajvnpmUse it when JSON Schema is authoritative, shared across languages, or compiled validation throughput matters.
arktypenpmUse it when ArkType's TypeScript-like syntax and type model are a better fit for the team.
yupnpmUse it mainly in an existing form stack already designed around Yup casting and schema mutation.

More utils guides

lru-cache · type-fest · ajv · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.