z.tojsonschema

Zod 4 z.toJSONSchema()

Use the native Zod 4 z.toJSONSchema() function to convert Zod schemas to JSON Schema, pick a draft target, control input vs output, add metadata, and migrate off the deprecated zod-to-json-schema package.

Zod to JSON Schema Converter

Quick answer: how do you convert a Zod schema to JSON Schema in Zod 4?

Call `z.toJSONSchema(schema)`. Zod 4 ships JSON Schema conversion in core, so you no longer need the external `zod-to-json-schema` package, which stopped active maintenance in November 2025. By default it emits Draft 2020-12; pass a `target` for Draft-07 or OpenAPI 3.0.

Basic z.toJSONSchema() usage

Import Zod and pass any schema. The function returns a plain JSON Schema object you can serialize, store, or feed to a validator. Object properties, required fields, and primitive types map over directly.

import * as z from "zod";

const UserSchema = z.object({
  id: z.string().uuid(),
  email: z.string().email(),
  age: z.number().int().optional(),
});

const jsonSchema = z.toJSONSchema(UserSchema);

// {
//   $schema: "https://json-schema.org/draft/2020-12/schema",
//   type: "object",
//   properties: {
//     id: { type: "string", format: "uuid" },
//     email: { type: "string", format: "email" },
//     age: { type: "integer" }
//   },
//   required: ["id", "email"],
//   additionalProperties: false
// }

Choose a draft target (Draft 2020-12, Draft-07, OpenAPI 3.0)

The biggest cause of validation failures after conversion is a draft mismatch. Set `target` to match the validator that consumes the schema. Draft 2020-12 is the default; choose Draft-07 for older Ajv setups and openapi-3.0 when pasting into an OpenAPI document.

// Draft-07 for a classic Ajv instance
const draft7 = z.toJSONSchema(UserSchema, { target: "draft-7" });

// OpenAPI 3.0 Schema Object
const openapi = z.toJSONSchema(UserSchema, { target: "openapi-3.0" });

Input vs output with the io option

Schemas with `.default()`, `.catch()`, or transforms have a different shape before and after parsing. By default Zod converts the output type. Pass `io: "input"` to describe the data a client should send instead of what your app receives after parsing.

const FormSchema = z.object({
  role: z.string().default("member"),
  acceptedAt: z.iso.datetime(),
});

// Describes parsed output (role always present)
const outputSchema = z.toJSONSchema(FormSchema);

// Describes the request payload a client sends (role optional)
const inputSchema = z.toJSONSchema(FormSchema, { io: "input" });

Add titles, descriptions, and examples with .meta()

Use `.meta()` to attach JSON Schema annotations. Metadata registered through `.meta()` lands in Zod's global registry and is included automatically in the converted output, which is how you produce self-documenting schemas for OpenAPI and AI structured outputs.

const Email = z
  .string()
  .email()
  .meta({
    title: "Email",
    description: "Primary contact email",
    examples: ["jane@acme.dev"],
  });

z.toJSONSchema(Email);
// {
//   type: "string",
//   format: "email",
//   title: "Email",
//   description: "Primary contact email",
//   examples: ["jane@acme.dev"]
// }

Migrate from the zod-to-json-schema package

The community `zod-to-json-schema` package is no longer actively maintained because the native function covers the same workflow. The migration is mostly a one-line import change. Drop the dependency, switch to the core import, and move the schema-name argument into `$ref` handling if you relied on it.

  • Remove `zod-to-json-schema` from package.json and delete its import lines.
  • Replace `zodToJsonSchema(schema, name)` with `z.toJSONSchema(schema)` and set `target` if you previously passed draft options.
  • If you bundled many schemas under `$defs`, convert them with a registry and the `reused: "ref"` option instead of the package name argument.
  • Re-run your validator tests after switching the draft target so format keywords still compile.
// Before (deprecated package)
import { zodToJsonSchema } from "zod-to-json-schema";
const schema = zodToJsonSchema(UserSchema, "User");

// After (native Zod 4)
import * as z from "zod";
const schema = z.toJSONSchema(UserSchema);

When to still use a converter tool instead

The native function is ideal inside application code. A browser converter is still useful for quick inspection, reviewing a teammate's schema, or generating JSON Schema without wiring up a Zod project, because it runs the same conversion behavior on pasted input.

Limitations to expect

JSON Schema cannot represent everything Zod can express, so conversion is lossy in predictable ways. Plan for these gaps before treating the output as a complete contract.

  • Runtime-only checks such as `.refine()` and `.transform()` are not expressed in the JSON Schema output.
  • Unrepresentable types (for example `z.bigint()`, `z.date()`, `z.map()`) throw unless you pass an `unrepresentable` fallback.
  • z.toJSONSchema() produces a schema object only, not OpenAPI paths, operations, responses, or security schemes.
  • Draft 2020-12 keywords like `prefixItems` will fail on validators that only understand Draft-07.

z.toJSONSchema() target options

TargetWhat it emitsUse when
draft-2020-12 (default)Modern JSON Schema using the 2020-12 dialect, including `$schema` and `prefixItems` for tuples.You consume the schema with an up-to-date validator such as Ajv 2020 or store contracts in your own tooling.
draft-7Draft-07 output that older validators and many existing toolchains still expect.Your downstream validator (default Ajv import, legacy services) targets Draft-07.
openapi-3.0An OpenAPI 3.0 Schema Object (nullable handled the 3.0 way, no 2020-12-only keywords).You paste the result into an OpenAPI 3.0 components/schemas block.

Use FrameworkKit to generate the starter code, then review the output before shipping it in production.

Zod to JSON Schema Converter

Zod to JSON Schema resources

FAQ

Does z.toJSONSchema replace the zod-to-json-schema package?

Yes for most use cases. Zod 4 includes native JSON Schema conversion, and the community package is no longer actively maintained. Switch the import to `z.toJSONSchema` and set a `target` if you relied on draft options.

Which draft target should I use?

Match the validator that consumes the schema. Use the default Draft 2020-12 for modern tooling, `draft-7` for older Ajv setups, and `openapi-3.0` when pasting into an OpenAPI 3.0 document.

Is z.toJSONSchema enough to generate an OpenAPI spec?

No. It converts a single schema into a JSON Schema or OpenAPI Schema Object. You still need to assemble paths, operations, request bodies, responses, and security schemes to produce a full OpenAPI document.

How do I add a description or title to the output?

Use `.meta({ title, description, examples })` on the schema. Metadata is stored in Zod's global registry and included automatically in the converted JSON Schema.

Why does conversion throw on some schemas?

Types like bigint, date, and map have no standard JSON Schema representation. Pass an `unrepresentable` fallback option or replace the field with a representable type before converting.

Related comparisons

Related tools