npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

x-to-zod

v0.10.2

Published

Enhanced fork of json-schema-to-zod - Converts JSON Schema into Zod schemas with fluent builder pattern

Downloads

699

Readme

X-to-Zod

NPM Version NPM Downloads

Note: This is an enhanced fork of json-schema-to-zod by Stefan Terdell. All credit for the original implementation goes to the original author and contributors.

Overview

A runtime package and CLI tool to convert JSON Schema draft-2020-12 objects or files into Zod schemas in the form of JavaScript code.

TypeScript Type Definitions: This package uses json-schema-typed for comprehensive JSON Schema draft-2020-12 type definitions, providing excellent IntelliSense and type safety. Earlier JSON Schema drafts (such as draft-04/06/07) may work when they use features that are compatible with draft-2020-12, but only draft-2020-12 is explicitly supported.

Looking for the exact opposite? Check out zod-to-json-schema

Enhancements in This Fork

This fork includes several architectural improvements and new features:

1. Fluent Builder Pattern Architecture

  • Complete rewrite of internal code generation using fluent builders
  • Consistent lazy evaluation pattern across all schema types
  • Builder classes mirror Zod's API: build.number(), build.string(), build.object(), etc.
  • Smart constraint merging (e.g., multiple .min() calls keep the strictest value)

2. Consolidated Modifier System

  • All builders extend BaseBuilder<T> with shared modifiers
  • Eliminated 154 lines of duplicated code
  • Consistent behavior for .optional(), .nullable(), .default(), .describe(), .brand(), .readonly(), .catch(), and more

3. Enhanced Zod v4 Support

  • Support for new Zod v4 types: void, undefined, date, bigint, symbol, nan
  • String validators: url, httpUrl, hostname, emoji, base64url, hex, jwt, nanoid, cuid, cuid2, ulid, ipv4, ipv6, mac, cidrv4, cidrv6, hash, isoDate, isoTime, isoDatetime, isoDuration, uuidv4, uuidv6, uuidv7
  • Collection types: set, map
  • Advanced types: promise, lazy, function, codec, preprocess, pipe, json, file, nativeEnum, templateLiteral, xor, keyof

4. Improved oneOf Handling

  • Simplified implementation using native z.xor() instead of manual superRefine
  • Cleaner generated code for exclusive OR (exactly one schema must match)

5. Better Module Resolution

  • Fixed ESM build configuration for proper TypeScript module resolution
  • Added moduleResolution: "bundler" for compatibility with modern bundlers

6. Type-Safe Builder Params

  • All Zod v4 builder factory functions accept properly typed params instead of unknown
  • String format builders (hex, hostname, jwt, mac, xid, ksuid, e164, base64url, httpUrl) accept Parameters<typeof z.X>[0]
  • Number format builders (int, float32, float64, int32, uint32) and BigInt builders (int64, uint64) fully typed
  • Full IntelliSense support — error options, constraints, and all Zod params are visible in IDE

7. Code Quality

  • Migrated to Vitest for faster test execution
  • Updated linting with oxlint
  • Better test coverage and organization
  • 90-test symmetry suite covering the full buildV4 factory

8. Zod v3/v4 Dual-Mode Support

  • Generate schemas compatible with either Zod v3 or v4 via zodVersion option
  • Defaults to 'v3' for backward compatibility
  • v4 mode generates new syntax: z.strictObject(), z.looseObject(), .extend() instead of .merge()
  • Fully backward compatible - existing code continues to work without changes

9. Post-Processing System

  • Transform Zod builders after parsing with custom post-processors
  • Type-based filtering to target specific builder types (objects, arrays, strings, etc.)
  • Path-based filtering for granular control
  • Use cases: add organization-wide validation rules, security constraints, custom transformations
  • See Post-Processing Guide for details

Installation

npm i -g x-to-zod

Usage

Version-Specific Imports

This package supports importing version-specific builder APIs. This ensures you only use features compatible with your target Zod version and provides TypeScript type safety.

Using Zod v4 (default)

import { build } from 'x-to-zod/v4';

// All v4 features available
const promiseSchema = build.promise(build.string());
const lazySchema = build.lazy('() => mySchema');
const jsonSchema = build.json();

Using Zod v3 (backward compatibility)

import { build } from 'x-to-zod/v3';

// Only v3-compatible features available
const stringSchema = build.string();
const objectSchema = build.object({ name: build.string() });

// TypeScript error - v4-only features not available:
// const promiseSchema = build.promise(build.string()); // ❌

Benefits:

  • Type Safety: TypeScript prevents using v4-only features when importing from v3
  • Explicit Intent: Makes your Zod version dependency clear in code
  • Future-Proof: Easier migration when Zod releases new versions

Default Import: The main package export includes all features (v4-compatible):

import { build } from 'x-to-zod';
// Same as 'x-to-zod/v4'

CLI

Simplest example

x-to-zod -i mySchema.json -o mySchema.ts

Example with $refs resolved and output formatted

npm i -g x-to-zod json-refs prettier

```console
json-refs resolve mySchema.json | x-to-zod | prettier --parser typescript > mySchema.ts

Options

| Flag | Shorthand | Function | | -------------- | --------- | ---------------------------------------------------------------------------------------------- | | --input | -i | JSON or a source file path. Required if no data is piped. | | --name | -n | The name of the schema in the output | | --depth | -d | Maximum depth of recursion in schema before falling back to z.any(). Defaults to 0. | | --module | -m | Module syntax; esm, cjs or none. Defaults to esm in the CLI and none programmatically. | | --type | -t | Export a named type along with the schema. Requires name to be set and module to be esm. | | --withJsdocs | -wj | Generate jsdocs off of the description property. |

Programmatic

Simple example

import { jsonSchemaToZod } from "x-to-zod";
const myObject = {
  type: "object",
    hello: {
      type: "string",
    },
  },
};

const module = jsonSchemaToZod(myObject, { module: "esm" });

// `type` can be either a string or - outside of the CLI - a boolean. If it's `true`,
// the name of the type will be the name of the schema with a capitalized first letter.
const moduleWithType = jsonSchemaToZod(myObject, {
  name: "mySchema",
  module: "esm",
  type: true,
});

const cjs = jsonSchemaToZod(myObject, { module: "cjs", name: "mySchema" });

const justTheSchema = jsonSchemaToZod(myObject);

Post-Processing

Transform Zod builders after parsing with post-processors:

typeFilter matches parser typeKind values (for example object, array, oneOf). It does not match builder class names such as ObjectBuilder.

import { jsonSchemaToZod } from "x-to-zod";
import { is } from "x-to-zod/utils";

const schema = {
  type: "object",
  properties: {
    name: { type: "string" },
    tags: { type: "array", items: { type: "string" } }
  }
};

// Make all objects strict
const result = jsonSchemaToZod(schema, {
  postProcessors: [
    {
      processor: (builder) => {
        if (is.objectBuilder(builder)) {
          return builder.strict();
        }
        return builder;
      },
      typeFilter: 'object'
    }
  ]
});

// Multiple processors with type filtering
const enhanced = jsonSchemaToZod(schema, {
  postProcessors: [
    // Make objects strict
    {
      processor: (builder) => is.objectBuilder(builder) ? builder.strict() : builder,
      typeFilter: 'object'
    },
    // Require non-empty arrays
    {
      processor: (builder) => is.arrayBuilder(builder) ? builder.min(1) : builder,
      typeFilter: 'array'
    }
  ]
});

See Post-Processing Guide for more examples and use cases.

Multi-Schema Projects

NEW in v0.6.0: Support for multi-schema projects with cross-schema references.

Work with multiple JSON Schemas that reference each other, automatically resolving $ref dependencies and generating organized TypeScript/Zod output files. Perfect for:

  • OpenAPI/Swagger components with shared schemas
  • Domain-Driven Design with separate schema files per domain entity
  • Monorepo projects with isolated schema packages

Quick Example

import { SchemaProject } from 'x-to-zod';

const project = new SchemaProject.SchemaProject({
  outDir: './generated',
  moduleFormat: 'both',       // Generate both ESM and CJS
  zodVersion: 'v4',
  generateIndex: true,
});

// Add schemas with cross-references
project.addSchema('user', {
  $id: 'user',
  type: 'object',
  properties: {
    id: { type: 'string' },
    name: { type: 'string' }
  }
});

project.addSchema('post', {
  $id: 'post',
  type: 'object',
  properties: {
    id: { type: 'string' },
    title: { type: 'string' },
    authorId: { $ref: 'user#/properties/id' }  // Cross-schema reference
  }
});

await project.build();
// Generates: generated/user.ts, generated/post.ts, generated/index.ts

CLI Project Mode

# Basic usage
x-to-zod --project --schemas "./schemas/*.json" --out ./generated

# With options
x-to-zod --project \
  --schemas "./schemas/users/*.json" \
  --schemas "./schemas/posts/*.json" \
  --out ./generated \
  --module-format both \
  --zod-version v4 \
  --generate-index

See Multi-Schema Projects Guide for complete documentation, API reference, and examples.

Builder API

The build.* factory creates fluent builders that mirror Zod's API. Each builder supports .text() to produce code and shares common modifiers like .optional(), .nullable(), .default(), .describe(), .brand(), .readonly(), .catch(), .refine(), .superRefine(), .meta(), .transform().

  • Primitives:

    • build.string()z.string()
    • build.number()z.number()
    • build.boolean()z.boolean()
    • build.bigint()z.bigint()
    • build.symbol()z.symbol()
    • build.nan()z.nan()
    • build.null()z.null()
    • build.undefined()z.undefined()
    • build.void()z.void()
  • Structured:

    • build.object(props)z.object({ ... })
      • Helpers: .strict(), .loose(), .catchall(schema), .superRefine(fn), .and(schema), .extend(schema|string), .merge(schema|string), .pick(keys), .omit(keys)
    • build.array(item)z.array(item)
      • Constraints: .min(n), .max(n), .length(n), .nonempty()
      • nonempty() delegates to .min(1) and accepts the same typed params
      • length(n) sets an exact size and supersedes any min/max on serialization
    • build.tuple(items)z.tuple(items)
    • build.record(key, value)z.record(key, value)
    • build.map(key, value)z.map(key, value)
    • build.set(item)z.set(item)
  • Enums and Literals:

    • build.enum(values)z.enum(values)
    • build.literal(value)z.literal(value)
    • build.nativeEnum(enumObj)z.nativeEnum(enumObj)
  • Unions and Intersections:

    • build.union(schemas)z.union(...)
    • build.intersection(a, b)z.intersection(a, b)
    • build.discriminatedUnion(tag, options)z.discriminatedUnion(tag, options)
    • build.xor(schemas)z.xor(schemas) (exactly one must match)
  • Functions and Lazy:

    • build.function()z.function()
      • .args(...schemas), .returns(schema)
    • build.lazy(getter)z.lazy()
  • Pipes and Transforms:

    • build.pipe(input)z.pipe()
    • build.preprocess(fn, schema)z.preprocess()
    • build.codec(parseFn, serializeFn)z.codec()
    • build.json(params?)z.json() — typed params for error messages
  • Zod v4 Format Builders (all accept typed params for error customisation):

    • String: build.hex(params?), build.hostname(params?), build.jwt(params?), build.mac(params?), build.e164(params?), build.xid(params?), build.ksuid(params?), build.base64url(params?), build.httpUrl(params?)
    • Number: build.int(params?), build.float32(params?), build.float64(params?), build.int32(params?), build.uint32(params?)
    • BigInt: build.int64(params?), build.uint64(params?)
  • Strings (validators):

    • build.string().url(), .httpUrl(), .hostname(), .emoji(), .base64url(), .hex(), .jwt(), .nanoid(), .cuid(), .cuid2(), .ulid(), .ipv4(), .ipv6(), .mac(), .cidrv4(), .cidrv6(), .hash(algorithm), .isoDate(), .isoTime(), .isoDatetime(), .isoDuration(), .uuidv4(), .uuidv6(), .uuidv7()
  • Numbers (constraints):

    • build.number().int(), .min(n), .max(n), .positive(), .negative(), .nonnegative(), .nonpositive(), .multipleOf(n)
  • Templates and Keys:

    • build.templateLiteral(parts)z.templateLiteral(parts)
    • build.keyof(obj)z.keyof(obj)

Examples

// Object with constraints and shared modifiers
build
  .object({ id: build.string().uuidv7(), name: build.string().min(1) })
  .strict()
  .default({ id: '...', name: '' })
  .text();

// XOR union (exactly one must match)
build.xor([build.string(), build.number()]).text();

// Function schema
build.function().args(build.string(), build.number()).returns(build.boolean()).text();
module
import { z } from "zod";

export default z.object({ hello: z.string().optional() });
moduleWithType
import { z } from "zod";

export const mySchema = z.object({ hello: z.string().optional() });
export type MySchema = z.infer<typeof mySchema>;
cjs
const { z } = require("zod");

module.exports = { mySchema: z.object({ hello: z.string().optional() }) };
justTheSchema
z.object({ hello: z.string().optional() });

Example with $refs resolved and output formatted

import { z } from "zod";
import { resolveRefs } from "json-refs";
import { format } from "prettier";
import jsonSchemaToZod from "x-to-zod";

async function example(jsonSchema: Record<string, unknown>): Promise<string> {
  const { resolved } = await resolveRefs(jsonSchema);
  const code = jsonSchemaToZod(resolved);
  const formatted = await format(code, { parser: "typescript" });

  return formatted;
}

Zod Version Support

This library supports generating schemas compatible with both Zod v3 and v4 through the zodVersion option.

Basic Usage

import { jsonSchemaToZod } from "x-to-zod";

// Generate Zod v3 code (default - backward compatible)
const schemaV3 = jsonSchemaToZod(mySchema);
// or explicitly
const schemaV3Explicit = jsonSchemaToZod(mySchema, { zodVersion: 'v3' });

// Generate Zod v4 code (opt-in for new features)
const schemaV4 = jsonSchemaToZod(mySchema, { zodVersion: 'v4' });

Key Differences

The zodVersion option affects how certain Zod constructs are generated:

Object Strict/Loose Modes

v3 mode (default):

// additionalProperties: false
z.object({ name: z.string() }).strict()

// passthrough behavior (using .loose() method)
z.object({ name: z.string() }).passthrough()

v4 mode:

// additionalProperties: false
z.strictObject({ name: z.string() })

// passthrough behavior
z.looseObject({ name: z.string() })

Object Merge

v3 mode (default):

baseObject.merge(otherObject)

v4 mode:

baseObject.extend(otherObject)

Error Messages (Future)

When implemented, error messages will use different parameter names:

v3 mode: { message: "error text" } v4 mode: { error: "error text" }

Builder API with Version Support

The builder API also respects the zodVersion option:

import { build } from "x-to-zod/builders";

// v4 mode
build.object({ name: build.string() }, { zodVersion: 'v4' }).strict().text()
// => 'z.strictObject({ "name": z.string() })'

// v3 mode (default)
build.object({ name: build.string() }).strict().text()
// => 'z.object({ "name": z.string() }).strict()'

Migration Guide

When to use v3 mode (default)

  • Existing projects using Zod v3
  • Want to avoid any breaking changes
  • Gradual migration to Zod v4

When to use v4 mode

  • New projects starting with Zod v4
  • Ready to adopt v4's improved API
  • Want cleaner generated code

Migration Steps

  1. Start with v3 mode (default) - your existing code continues to work
  2. Test thoroughly - ensure all generated schemas work as expected
  3. Switch to v4 mode - set zodVersion: 'v4' when ready
  4. Update consuming code - adjust for any Zod v4 API changes
  5. Enjoy improved syntax - benefit from cleaner, more concise schemas

Compatibility Notes

  • Default is v3 for backward compatibility
  • Both modes are fully tested and production-ready
  • No runtime dependencies on specific Zod versions - generates code strings only
  • Mix and match - you can generate different schemas with different versions as needed

Advanced Features

Custom Parsers

Register custom parsers for schema types not handled by the built-in parsers. Custom parsers are the primary extension point for customizing code generation.

Using registerParser()

import { registerParser, AbstractParser, jsonSchemaToZod } from "x-to-zod";

// Define a custom parser class
class CustomDateTimeParser extends AbstractParser<object, 'custom-datetime'> {
  readonly typeKind = 'custom-datetime' as const;

  protected parseImpl(schema) {
    return this.refs.build.code('z.string().datetime()');
  }
}

// Register it globally
registerParser('custom-datetime', CustomDateTimeParser);

// Now schemas with type: 'custom-datetime' dispatch to your parser
const code = jsonSchemaToZod({ type: 'custom-datetime' } as any);
// Output: z.string().datetime()

Custom parsers extend AbstractParser and receive full access to:

  • this.schema — the current schema node
  • this.refs — context with path tracking, build factory, and options
  • this.parseChild(schema, ...path) — recursive parsing of child schemas
  • Automatic transformers and postProcessors pipeline integration

Schema Transformers

The transformers option transforms JSON schema nodes before parsing. Use this for:

  • Normalizing vendor-specific schema extensions
  • Applying global schema transformations
  • Removing or modifying unsupported keywords
  • Injecting default values or constraints

Function Signature

import type { SchemaTransformer, Context } from 'x-to-zod';

// SchemaTransformer is a callable with an optional pathPattern property, for example:
const transformer: SchemaTransformer = (schema: object, refs: Context): object | undefined => {
  // perform any transformation you need here
  return schema;
};

transformer.pathPattern = ['/paths/*'];

Return Values:

  • object: The transformed schema (replaces the original)
  • undefined: Keep the schema unchanged

Example: Normalize Vendor Extensions

import { jsonSchemaToZod } from "x-to-zod";

const schema = {
  type: 'object',
  properties: {
    name: {
      type: 'string',
      'x-custom-min': 5,
      'x-custom-max': 100
    }
  }
};

const code = jsonSchemaToZod(schema, {
  transformers: [
    (schema, refs) => {
      if (schema['x-custom-min'] !== undefined) {
        return {
          ...schema,
          minLength: schema['x-custom-min'],
          maxLength: schema['x-custom-max']
        };
      }
    }
  ]
});

// Output includes: z.string().min(5).max(100)

Example: Multiple Transformers with Path Filtering

Transformers execute in order and support pathPattern for targeted application:

const code = jsonSchemaToZod(schema, {
  transformers: [
    // Normalize vendor extensions everywhere
    (schema) => {
      if (schema['x-nullable']) {
        return { ...schema, nullable: true };
      }
    },
    // Apply default constraints to strings
    Object.assign(
      (schema) => {
        if (schema.type === 'string' && !schema.maxLength) {
          return { ...schema, maxLength: 1000 };
        }
      },
      { pathPattern: 'properties.*' }  // Only under properties
    ),
    // Strip unsupported keywords
    (schema) => {
      const { $comment, examples, ...rest } = schema;
      return rest;
    }
  ]
});

Combining Transformers and Post-Processors

The full pipeline runs in this order:

Schema → transformers → Parser → postProcessors → Builder
         (Schema→Schema)         (Builder→Builder)

Use transformers to modify schemas before parsing and postProcessors to modify builders after parsing:

const code = jsonSchemaToZod(schema, {
  // Transform schema structure first
  transformers: [
    (schema) => {
      if (schema['x-custom-type']) {
        return { type: schema['x-custom-type'], ...schema };
      }
    }
  ],
  // Then modify the generated builder
  postProcessors: [
    {
      processor: (builder, context) => {
        if ((context.schema as any).type === 'custom-type') {
          return context.build.any();
        }
      },
      pathPattern: 'properties.*'
    }
  ]
});

Migration from Previous Versions

If you were using parserOverride or preprocessors, update your code:

| Before | After | |--------|-------| | parserOverride: fn | Use registerParser() for type-based overrides, or postProcessors with pathPattern for path-specific overrides | | preprocessors: [...] | transformers: [...] (same signature, renamed) | | preProcessors: [...] | transformers: [...] (same signature, renamed) |

Documentation

Comprehensive Guides

  • Parser Architecture - Understanding the class-based parser system

    • Class hierarchy and template method pattern
    • Parser selection algorithm
    • Type guards and symmetric parse API
    • How to add new parser classes
  • Post-Processing Guide - Transform builders after parsing

    • Post-processor concepts and use cases
    • Type and path filtering
    • Practical examples (strict objects, non-empty arrays, custom validations)
    • Best practices and debugging tips
  • Migration Guide - For contributors extending parsers

    • Functional vs class-based comparison
    • Migration steps and patterns
    • Testing strategies
    • FAQs
  • API Reference - Complete API documentation

    • BaseParser and all parser classes
    • Registry functions and parse API
    • Type definitions and type guards
    • Usage examples

Quick Links

Important Notes

Schema Factoring

Factored schemas (like object schemas with "oneOf" etc.) is only partially supported. Here be dragons.

Use at Runtime

The output of this package is not meant to be used at runtime. JSON Schema and Zod does not overlap 100% and the scope of the parsers are purposefully limited in order to help the author avoid a permanent state of chaotic insanity. As this may cause some details of the original schema to be lost in translation, it is instead recommended to use tools such as Ajv to validate your runtime values directly against the original JSON Schema.

That said, it's possible in most cases to use eval. Here's an example that you shouldn't use:

const zodSchema = eval(jsonSchemaToZod({ type: "string" }, { module: "cjs" }));

zodSchema.safeParse("Please just use Ajv instead");

Credits

This is a fork of json-schema-to-zod by Stefan Terdell.

Type Definitions

JSON Schema TypeScript type definitions provided by json-schema-typed by Thomas Aribart, supporting JSON Schema draft-2020-12.

Original Contributors

Original contributors include:

  • Chen (https://github.com/werifu)
  • Nuno Carduso (https://github.com/ncardoso-barracuda)
  • Lars Strojny (https://github.com/lstrojny)
  • Navtoj Chahal (https://github.com/navtoj)
  • Ben McCann (https://github.com/benmccann)
  • Dmitry Zakharov (https://github.com/DZakh)
  • Michel Turpin (https://github.com/grimly)
  • David Barratt (https://github.com/davidbarratt)
  • pevisscher (https://github.com/pevisscher)
  • Aidin Abedi (https://github.com/aidinabedi)
  • Brett Zamir (https://github.com/brettz9)
  • vForgeOne (https://github.com/vforgeone)
  • Adrian Ordonez (https://github.com/adrianord)
  • Jonas Reucher (https://github.com/Mantls)

License

ISC