Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

zod-aot

Package Overview
Dependencies
Maintainers
1
Versions
64
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

zod-aot

Compile Zod schemas into zero-overhead validation functions at build time

latest
Source
npmnpm
Version
0.20.3
Version published
Weekly downloads
1.4K
-6.65%
Maintainers
1
Weekly downloads
 
Created
Source

Zod AOT

Compile Zod schemas into zero-overhead validation functions at build time.

CI codecov npm License: MIT

Keep your existing Zod schemas. Get 2-64x faster validation. No code changes required.

// vite.config.ts — add one line
import zodAot from "zod-aot/vite";
export default defineConfig({
  plugins: [zodAot({ autoDiscover: true })],
});
// src/schemas.ts — write plain Zod, nothing else
import { z } from "zod";

export const UserSchema = z.object({
  name: z.string().min(3),
  email: z.email(),
  age: z.number().int().positive(),
});

// Use it anywhere — tRPC, Hono, React Hook Form, etc.
// At build time, zod-aot compiles it to a 3-64x faster validator.

Install

npm install zod-aot zod@^4
RuntimeVersion
Node.js20+
Bun1.3+
Deno2.0+

Usage

There are three ways to use zod-aot. Choose the one that fits your project.

The plugin automatically detects and compiles all exported Zod schemas at build time. No wrappers, no imports from zod-aot in your source code.

vite.config.ts:

import zodAot from "zod-aot/vite";

export default defineConfig({
  plugins: [zodAot({ autoDiscover: true })],
});

Your schema file stays pure Zod:

// src/schemas.ts
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(),
});

export const ListUsersSchema = z.object({
  page: z.number().int().min(1).optional().default(1),
  limit: z.number().int().min(1).max(100).optional().default(20),
});

Use them as usual:

const user = CreateUserSchema.parse(data);          // throws on failure
const result = CreateUserSchema.safeParse(data);    // { success, data/error }

At build time, the plugin:

  • Finds every file with import ... from "zod" (skips type-only imports)
  • Executes the file and detects exported Zod schemas
  • Compiles each schema into an optimized validator
  • Replaces the export with a tree-shakeable IIFE that preserves the full Zod API

What "preserves the full Zod API" means: The compiled schema inherits from the original Zod schema via Object.create(). So ._zod, .shape, Standard Schema (~standard), instanceof checks — all still work. Libraries that accept Zod schemas (tRPC, Hono, React Hook Form) work without changes.

2. compile() (Explicit)

If you prefer explicit opt-in, wrap specific schemas with compile():

import { z } from "zod";
import { compile } from "zod-aot";

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 validation
// After build: uses AOT-compiled optimized code
validateUser.parse(data);
validateUser.safeParse(data);

compile() and autoDiscover coexist — compile() schemas are detected first, then autoDiscover picks up remaining plain Zod exports.

3. CLI (No Bundler)

Generate optimized validation files from the command line:

# Single file
npx zod-aot generate src/schemas.ts -o src/schemas.compiled.ts

# Directory
npx zod-aot generate src/ -o src/compiled/

# Watch mode
npx zod-aot generate src/ --watch

Build Plugin

Supported Build Tools

Build ToolImport
Viteimport zodAot from "zod-aot/vite"
webpackimport zodAot from "zod-aot/webpack"
esbuildimport zodAot from "zod-aot/esbuild"
Rollupimport zodAot from "zod-aot/rollup"
Rolldownimport zodAot from "zod-aot/rolldown"
rspackimport zodAot from "zod-aot/rspack"
Bunimport zodAot from "zod-aot/bun"
Farmimport zodAot from "zod-aot/farm"

Options

OptionTypeDefaultDescription
autoDiscoverbooleanfalseAuto-detect all exported Zod schemas without compile()
includestring[]Only process files matching these substrings
excludestring[]Skip files matching these substrings
zodCompatbooleantruePreserve Zod API via Object.create(). Set false for smaller output
verbosebooleanfalseLog per-schema compilation status during build
zodAot({
  autoDiscover: true,
  include: ["src/schemas"],
  verbose: true,
})

Bundle Size & Cross-File Dedup

Generated validators share a small runtime helper layer (__mkv validator wrapper, issue factories like __zaTS/__zaIT, and well-known regexes for email, uuid, cuid, ipv4, etc.).

On bundlers that support virtual modules — Vite, Rollup, Rolldown, esbuild, Farm, Bun — the plugin imports these helpers from virtual:zod-aot/runtime, so the bundler emits a single bundle-wide copy regardless of how many files reference them. webpack and rspack fall back to self-contained file-level helpers (a few hundred bytes per file) since they reject the virtual: URI scheme at the resolver layer.

The result: a 5-file project with 10 schemas all using z.email() and z.uuid() produces a bundle where each shared regex appears exactly once. Set zodCompat: false to additionally drop the original Zod schema reference when you don't need instanceof / .shape access on the compiled output.

autoDiscover: Side Effects Warning

With autoDiscover, the plugin executes files to inspect their exports. If a file imports Zod AND has side effects (starts a server, connects to a database), those side effects run at build time.

Fix: Use include to limit which files are scanned:

zodAot({
  autoDiscover: true,
  include: ["src/schemas", "src/validators"],
})

autoDiscover vs compile()

autoDiscovercompile()
Source code changesNoneWrap each schema
zod-aot import neededNoYes
What gets compiledAll exported Zod schemasOnly wrapped schemas
Build-time file executionFiles with import ... from "zod"Files with import ... from "zod-aot"
Best forNew projects, framework integrationGradual adoption, selective optimization

Framework Examples

tRPC

// src/schemas.ts
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),
});

// src/router.ts
import { CreateUserSchema } from "./schemas";

export const appRouter = t.router({
  createUser: t.procedure
    .input(CreateUserSchema)
    .mutation(({ input }) => createUser(input)),
});

With autoDiscover: true, CreateUserSchema is compiled at build time. The tRPC router uses the optimized version automatically. No .input(compile(CreateUserSchema)) needed.

Hono

import { Hono } from "hono";
import { zValidator } from "@hono/zod-validator";
import { UserSchema } from "./schemas";

const app = new Hono();

app.post("/users", zValidator("json", UserSchema), (c) => {
  const user = c.req.valid("json");
  return c.json(user);
});

React Hook Form

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { UserSchema } from "./schemas";

function UserForm() {
  const form = useForm({
    resolver: zodResolver(UserSchema),
  });
  // ...
}

Any Standard Schema Consumer

zod-aot compiled schemas implement Standard Schema via prototype chain. Any library that accepts Standard Schema validators works automatically.

Schema Diagnostics

Analyze your schemas before compiling — check coverage, Fast Path eligibility, and get actionable hints:

npx zod-aot check src/schemas.ts

Output:

src/schemas.ts

  CreateUserSchema  [Fast Path]  100% compiled  (5 checks)
  ├─ name: string (min_length, max_length)
  ├─ email: string (string_format[email])
  ├─ age: number (number_format[safeint], greater_than)
  └─ role: enum

  OrderSchema  [Slow Path]  85% compiled  (3 checks)
  ├─ id: string (string_format[uuid])
  └─ metadata: object
      └─ audit: fallback(transform)
         Hint: Consider z.pipe() to keep inner schema compilable

  Summary: 2 schemas | 1 Fast Path, 1 Slow Path | 8/9 nodes (88.9%)

CI Integration

# JSON output
npx zod-aot check src/schemas.ts --json

# Fail if any schema below 80% coverage
npx zod-aot check src/schemas.ts --json --fail-under 80
FlagDescription
--jsonStructured JSON output
--fail-under <pct>Exit code 1 if coverage below threshold
--no-colorDisable colored output

What Gets Compiled

Fully Compiled (3-64x faster)

string, number, bigint, boolean, null, undefined, any, unknown, literal, enum, date, object, array, tuple, record, set, map, union, discriminatedUnion, intersection, pipe (non-transform), optional, nullable, readonly, default, catch, coerce, templateLiteral, symbol, void, nan, never, lazy (self-recursive)

All standard Zod checks are supported: min, max, length, email, url, uuid, regex, int, positive, negative, multipleOf, int32, uint32, float32, float64, includes, startsWith, endsWith, and more.

Falls Back to Zod (Still Works, Not Faster)

These types contain JavaScript closures that cannot be compiled to static code:

TypeWhyAlternative
transformRuntime data transformation functionUse z.pipe() when possible
refine / superRefineCustom validation closuresUse built-in checks when possible
customArbitrary validation logic
preprocessInput preprocessing functionUse z.coerce when possible
lazy (non-recursive)Cannot resolve inner typeUse self-referencing lazy for recursion

Partial fallback: If an object has 10 properties and 1 uses transform, the other 9 are still compiled. Only the transform property falls back to Zod.

Tip: Run npx zod-aot check to see exactly which parts of your schemas are compiled and which fall back.

Benchmarks

5-way comparison: Zod v3 vs Zod v4 vs Zod AOT vs Typia vs AJV

ScenarioZod v3Zod v4Zod AOTTypiaAJVvs Zod v4
simple string8.7M10.0M11.0M11.0M11.1M1.1x
string (min/max)8.3M6.0M11.1M11.3M9.5M1.8x
number (int+positive)8.2M5.8M10.9M10.7M10.9M1.9x
enum8.0M9.5M10.6M10.6M10.5M1.1x
bigint (min/max)8.0M6.0M10.9M1.8x
tuple [string, int, bool]4.0M4.4M10.5M10.8M10.5M2.4x
record<string, number>2.3M1.9M5.4M7.5M9.4M2.8x
set<string> (5 items)2.7M1.6M9.8M6.3x
set<string> (20 items)1.0M475K7.7M16x
map<string, number> (5 entries)1.5M946K8.5M9.0x
map<string, number> (20 entries)490K238K5.2M22x
pipe (non-transform)6.3M3.8M10.8M2.8x
discriminatedUnion (3 variants)2.3M2.9M9.8M10.4M5.6M3.4x
medium object (valid)1.3M1.7M5.4M7.3M5.0M3.1x
medium object (invalid)351K65K471K2.1M5.6M7.3x
large object (10 items)82K111K4.0M4.1M834K36x
large object (100 items)9.0K11.6K676K808K89K58x
recursive tree (7 nodes)424K1.5M6.4M8.1M3.1M4.1x
recursive tree (121 nodes)24K101K741K1.4M250K7.4x
event log (combined)260K479K4.5M9.4x
partial fallback obj (transform)817K1.4M3.3M2.3x
partial fallback arr 10 (transform)88K139K841K6.1x
partial fallback arr 50 (transform)18K28K175K6.3x

ops/s, higher is better. "—" = not supported by the library. Measured with vitest bench on Apple M-series.

Performance scales with schema complexity. Nested objects and arrays see the biggest gains because zod-aot eliminates per-node traversal overhead. discriminatedUnion uses O(1) switch dispatch instead of Zod's sequential trial. Partial fallback schemas (containing transform/refine) still show 2-6x speedups.

pnpm bench   # run locally

Performance Architecture

For eligible schemas, zod-aot generates a two-phase validator:

  • Fast Path — A single && expression chain that validates the entire input with zero allocations. Valid input returns immediately.
  • Slow Path — Error-collecting validation that only runs when the Fast Path fails.

Additional optimizations: check ordering (cheap checks first), pre-compiled regex, Set-based enum lookups, small enum inlining (=== for 1-3 values).

Run npx zod-aot check --json to see which schemas qualify for Fast Path.

License

MIT

Keywords

zod

FAQs

Package last updated on 01 Jun 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts