Skip to content
Schema conversion

JSON Schema ⇄ Zod Converter

Generate a Zod schema from JSON Schema, or write JSON Schema back out from Zod code. Maps required to .optional(), plus enums, unions, nested objects, string formats and length constraints in both directions. Anything that cannot be converted is reported as a warning. Everything runs in your browser and the schema you paste is never sent anywhere.

Guide: How to use & features

  • Choose "JSON Schema → Zod" and paste a schema to get a `z.object({ ... })` definition plus a `z.infer` type alias.
  • Choose "Zod → JSON Schema" for the reverse. Leading `import` lines and `export const user =` are fine — the parser picks out the expression starting at `z.`.
  • Properties missing from `required` get `.optional()`, and in the reverse direction anything without `.optional()` is collected into `required`.
  • String details map both ways too: `format: "email"` becomes `.email()`, `minLength` becomes `.min()`.
  • Anything that cannot be converted, such as `$ref` or `allOf`, is reported as a warning under the output rather than silently producing a broken result.

FAQ: FAQ

  • How is this different from the JSON to Zod tool?

    The input is different. JSON to Zod infers types from a sample value such as an API response, so it cannot know which fields are required or what string formats apply. This tool takes JSON Schema itself, so constraints like required, enum, minLength and format:email carry through into the Zod chain (.optional() / .enum() / .min() / .email()). If you already have an OpenAPI or JSON Schema definition, this is the accurate path.
  • How do required and .optional() line up?

    The defaults are inverted between the two. JSON Schema treats a property as required only when it is listed in the required array, whereas Zod treats a property as required unless it carries .optional(). The converter absorbs that: going to Zod, properties missing from required get .optional(); coming back, properties without .optional() are collected into the required array.
  • Can it handle schemas that use $ref?

    References are not resolved. A $ref becomes z.any() and a warning is shown. If your schema factors shared definitions into $defs, either inline them before pasting or fix up that part of the generated output by hand. allOf is also reported, because an intersection cannot be expressed here — only the first subschema is converted.
  • Can I use the generated Zod schema for production validation?

    It works as a skeleton, but business rules that were never in the JSON Schema — allowed email domains, cross-field consistency — will not appear. Also note that a pattern becomes z.string().regex(), which can behave differently if the original regular expression is not ECMAScript-compatible. Always review the output before shipping it.

Use cases: Common use cases

  • Turn an OpenAPI definition into front-end types and validation

    The components.schemas section of OpenAPI is JSON Schema, so pasting it here gives you a Zod schema plus a z.infer type in one step — and keeps the server contract and client validation from drifting apart.
  • Export an existing Zod schema as an API contract

    If the Zod schema came first in your codebase, run the conversion the other way to produce JSON Schema you can drop into OpenAPI components or your docs.
  • Migrate hand-written validation to schema-driven checks

    When replacing a pile of if statements, an existing JSON Schema gives you a complete starting Zod schema — far fewer missed required fields and enums.
  • Make an AI-generated JSON Schema usable

    Paste a schema produced by an LLM and convert it into a Zod schema that actually compiles, giving you both static types and runtime validation.

Notes: Notes & limitations

  • $ref, allOf and conditionals are not expanded

    A $ref is not resolved and becomes z.any(); allOf converts only its first subschema. if/then/else and not are unsupported. All of these are surfaced as warnings under the output so you can fill in those parts by hand.
  • Zod → JSON Schema parses your code as text

    To keep everything in the browser, the code is never executed — it is parsed as text. z.object / z.array / z.union / z.enum / z.literal / z.record and the common method chains are supported, but a schema referenced through a variable, or an arbitrary function such as .refine(), cannot be represented in JSON Schema and is ignored.
  • Regular expressions in pattern are carried over verbatim

    A JSON Schema pattern becomes z.string().regex(), and the reverse puts the source back into pattern. JSON Schema permits regular expressions that are not ECMAScript-compatible, so a direct move can change behaviour.
  • Always review the generated output

    This tool produces a starting point for a migration. Business rules and cross-field consistency checks are not generated unless they were expressed in the original schema, so review the result before relying on it in production.

Articles for this tool

Recent Articles