zod-compiler is a new build-time plugin that compiles Zod schemas into zero-overhead validators. Install the plugin, keep your existing Zod code, and get 2–75x faster validation with zero runtime dependencies.
zod-compiler is a build-time plugin by Gajus Kuizinas (author of Slonik and many other TypeScript tools) that compiles Zod schemas into optimized, tree-shakeable validator functions. It works with Vite, webpack, esbuild, Rollup, Rolldown, rspack, Bun, and Farm — basically any modern bundler.
The key insight is that Zod runtime validation walks a schema tree on every call:
for each z.string(), z.object(), and z.union(),
Zod traverses its internal representation and checks types one node at a time. A compiled
validator flattens this traversal into a single boolean expression — or a switch
dispatch for unions — eliminating all per-node overhead.
Version 1.13.1 was published to npm on June 17, 2026, and the project has 322+ stars on GitHub with active development. It has been tested in production projects with tens of thousands of Zod schemas.
z.object() schemas.parse(), .safeParse(), .shape, Standard Schema all work.is() type guard on eligible schemasnpm install --save-dev zod-compiler
# or
pnpm add -D zod-compiler
# or
yarn add -D zod-compiler
For Vite (the most common setup):
// vite.config.ts
import { defineConfig } from "vite";
import zodCompiler from "zod-compiler/vite";
export default defineConfig({
plugins: [zodCompiler()],
});
For other bundlers, the import path changes:
| Bundler | Import |
|---|---|
| Vite | zodCompiler from "zod-compiler/vite" |
| webpack | zodCompiler from "zod-compiler/webpack" |
| esbuild | zodCompiler from "zod-compiler/esbuild" |
| Rollup | zodCompiler from "zod-compiler/rollup" |
| Rolldown | zodCompiler from "zod-compiler/rolldown" |
| rspack | zodCompiler from "zod-compiler/rspack" |
| Bun | zodCompiler from "zod-compiler/bun" |
| Farm | zodCompiler from "zod-compiler/farm" |
In auto mode (the default), the plugin scans every file that imports
from "zod", detects exported Zod schemas, and compiles them at build time.
Your schema files stay pure Zod:
// src/schemas/user.ts — unchanged, pure Zod
import { z } from "zod";
export const CreateUserSchema = z.object({
name: z.string().min(1).max(100),
email: z.email(),
age: z.number().int().min(0).max(150),
role: z.enum(["admin", "editor", "viewer"]),
});
export const UpdateUserSchema = z.object({
name: z.string().min(1).max(100).optional(),
email: z.email().optional(),
});
Usage code also stays the same:
import { CreateUserSchema } from "./schemas/user";
const user = CreateUserSchema.parse(data); // compiles to optimized validator
const result = CreateUserSchema.safeParse(data); // { success, data/error }
The whole point of zod-compiler is that you don't have to think about it.
Install the plugin, configure it once in your build file, and every Zod schema
in your project is automatically compiled. The API surface — .parse(),
.safeParse(), .parseAsync(), .safeParseAsync() —
remains identical.
As shown above: add the plugin, and every exported Zod schema in your project compiles automatically. A static pre-filter skips files whose exports provably can't be schemas (functions, components, constants) without executing them.
Auto mode also enables schema hoisting — schemas defined inside function bodies (React components, request handlers, helpers) are moved to module scope:
// Before: schema rebuilt on every call
function getSchema() {
return z.object({ name: z.string() });
}
// After (build output): schema constructed once per module load
const _zh_schema = z.object({ name: z.string() });
function getSchema() {
return _zh_schema;
}
This means a schema inside a React component that validates props, or inside a request handler that validates input — is built once at module load time, not on every invocation. The compiler also deduplicates identical schemas to a single binding.
compile()
If you prefer opt-in, wrap specific schemas with compile():
import { z } from "zod";
import { compile } from "zod-compiler";
const UserSchema = z.object({
name: z.string().min(3),
email: z.email(),
});
export const validateUser = compile(UserSchema);
// In dev: falls back to Zod's runtime
// After build: AOT-compiled optimized validator
validateUser.parse(data);
Pair it with schemas: "explicit" in plugin options to make
compile() the only compilation path — no automatic scanning needed.
This is ideal for gradual adoption in large codebases.
For projects without a bundler setup, or for one-off compilation:
# Single file
npx zod-compiler generate src/schemas.ts -o src/schemas.compiled.ts
# Directory
npx zod-compiler generate src/ -o src/compiled/
# Watch mode
npx zod-compiler generate src/ --watch
# Strip unknown keys from z.object() output
npx zod-compiler generate src/ --strip-unknown-keys
The benchmark compares zod-compiler against Zod v3, Zod v4, Typia, and AJV. Results are in ops/s (higher is better), measured on Apple M4 Max.
| Scenario | Zod v4 | zod-compiler | vs Zod v4 |
|---|---|---|---|
| Simple string | 14.4M | 16.2M | 1.1x |
| String (min/max) | 8.0M | 17.2M | 2.2x |
| Number (int+positive) | 7.8M | 15.7M | 2.0x |
| Enum | 12.3M | 16.9M | 1.4x |
| Tuple [string, int, bool] | 6.5M | 17.0M | 2.6x |
| Set<string> (20 items) | 695K | 12.1M | 17x |
| Map<string, number> (20 entries) | 361K | 8.6M | 24x |
| Discriminated union (3 variants) | 4.0M | 16.1M | 4.0x |
| Medium object (valid) | 2.4M | 10.3M | 4.3x |
| Medium object (invalid) | 80K | 15.5M | 194x |
| Large object (100 items) | 19K | 1.4M | 73x |
| Deeply nested object (243 leaves) | 19K | 1.2M | 64x |
| Recursive tree (121 nodes) | 142K | 2.3M | 16x |
The biggest wins come from nested objects, large schemas, and invalid input (where Zod builds expensive error objects while the compiled validator defers error materialization). The invalid-object case reaches 194x because the Fast Path rejects invalid input with a single boolean check — the expensive error tree is never constructed.
zod-compiler uses a two-phase validator:
&& boolean expression chain that validates the entire input with zero allocations. Valid input returns immediately..error triggers the full walk.
Additional optimizations: check ordering (cheapest checks first), pre-compiled regex,
Set-based enum lookups, small enum inlining (≤5 values compiled as ===
chains), discriminated-union O(1) switch dispatch, and auto-discrimination of plain
z.unions of tagged objects into the same switch architecture.
.is() Type Guard
Compiled schemas expose an additional method: .is(input): input is T.
For most schemas (objects, primitives, arrays, enums without coerce,
default, catch, or transform), this is the
cheapest possible “does this match?” check:
// Instead of safeParse().success:
if (UserSchema.is(data)) {
data.email; // narrowed to schema's output type
}
// Filtering invalid items:
const valid = items.filter((x) => UserSchema.is(x));
// Even works in .filter() callbacks:
The .is() guard returns a boolean with zero allocations — no
SafeParseResult object, no issues array, no error tree. It's on par with
Typia's is<T>() and a clean replacement for
schema.safeParse(x).success.
zod-compiler is designed for minimal bundle impact:
virtual:zod-compiler/runtime), emitted exactly once per bundle.email, uuid, ipv4 etc. appear once across the entire bundle.Address or Money) share their error-collecting walk function.output: "bag" to drop the original Zod schema reference when you don't need instanceof or .shape access.
On a realistic schema set where User/Company/Order/Invoice
reuse Address/Money/Contact, generated output drops
~50% raw / ~34% gzipped with no change to validation behavior.
The compiled schema preserves full Zod API compatibility. The optimized
parse/safeParse/parseAsync/safeParseAsync
methods are installed directly on the original schema object, which is exported as-is:
._zod — intact.shape — intact~standard) — intactinstanceof checks — intact.meta() / z.globalRegistry — intactz.toJSONSchema() — intactThis means libraries that accept Zod schemas work without changes:
zodResolver uses the compiled .parseIn auto mode, the plugin executes files to inspect their exports. If a schema file has side effects (starts a server, connects to a database), those run at build time. zod-compiler guards against this:
process.env.ZOD_COMPILER so cooperating modules can skip validationprocess.exit — an unguarded exit becomes a normal load failure, the build does not crash
The easiest fix is to use include to limit which files are scanned:
zodCompiler({
include: ["src/schemas", "src/validators"],
});
zod-compiler has a persistent transform cache at node_modules/.cache/zod-compiler.
Cache entries self-validate against dependency content hashes — restoring a stale cache
causes recompiles, never stale output. Use GitHub Actions cache to persist it across runs:
- uses: actions/cache@v4
with:
path: node_modules/.cache/zod-compiler
key: zod-compiler-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: zod-compiler-${{ runner.os }}-
For large existing codebases, start with schemas: "explicit" and wrap only
the hottest paths — the schemas validated most frequently or on the most data.
API input validation, database row validation, and form submission handlers are
typically the highest-impact targets.
Run npx zod-compiler check --json to see which schemas in your project
qualify for the Fast Path (and which fall back due to captured transforms or other
limitations).
zod-compiler is one of those rare tools that delivers a dramatic performance improvement with zero developer effort. Install the plugin, keep all your existing Zod schemas, and your validation gets 2–75x faster across the board. No API changes, no migration work, no runtime dependencies.
The two-phase validator architecture — fast path for valid input, lazy error materialization for failures — means that the common case (most inputs pass validation) is as fast as a hand-written type guard. The schema hoisting feature alone eliminates an entire class of performance bugs where schemas are rebuilt on every React render or every request handler invocation.
If your project uses Zod, try zod-compiler. Run pnpm benchmark locally
to see the improvement on your own hardware. The numbers speak for themselves.
Building a data-heavy application that needs efficient validation? I can help architect and implement it. Free initial consultation.