Converting JSON to a Zod schema produces reviewable TypeScript source code — an import, a named schema expression, and an inferred type — derived strictly from the structure of one JSON sample. The output is generated locally in the browser, never uploaded, and never executed on the page. Required and optional fields are decided by what the sample actually contains: a key seen in every observed object becomes required, a key missing from any object becomes optional, a null beside another type becomes nullable, and a mixed-type array becomes a deterministic z.union. The schema is a starting point, not a contract proof: string formats, numeric ranges, integer constraints, enums, discriminated unions, dates, URLs, defaults, refinements, and business rules are deliberately not guessed and must come from authoritative documentation. Install and pin the Zod version you target inside your own project, compile the generated source, and run accepted and rejected cases against it before treating the result as a real contract.

convert json to zod schema
convert json to zod schema

What the Converter Emits From One JSON Sample

The JSON to Zod Schema Converter accepts a single valid JSON value — an object, an array, or a primitive — together with a JavaScript identifier used as the exported schema and inferred type name. After parsing, the page emits a small, dependency-free source snippet: the import { z } from "zod" line, a schema declaration of the form export const <Name> = z.object({ ... }) (or the appropriate array, primitive, or union expression), and a companion export type <Name> = z.infer<typeof <Name>> declaration. The page does not load Zod, does not execute the generated source, and does not transmit the sample anywhere. The result is plain text you can review, format, and copy into a project that already controls its own Zod version.

Object properties follow source encounter order, so the generated z.object block reads in the same sequence as the JSON you pasted. Keys that are valid JavaScript identifiers stay unquoted in the output. Keys that contain spaces, hyphens, leading digits, quotes, or other characters unsafe as identifiers are emitted with JSON string escaping so the surrounding object literal remains valid TypeScript source. Numbers and other values are not rewritten, so what you paste is what gets inferred.

Convert JSON to Zod Schema: The Three-Step Workflow

  1. Paste one valid and representative JSON value into the input area and enter a valid JavaScript identifier for the schema. The identifier becomes the exported constant name and the inferred TypeScript type name, so pick something that matches the noun your code uses for the payload.
  2. Generate the Zod source, then inspect the output for optional, nullable, union, numeric, and empty-array choices. Confirm required fields line up with the real contract, decide whether nullable markers match your API, and check that mixed arrays and empty arrays behave the way you expect.
  3. Copy the source into a project that has the Zod version you want installed, format the snippet, compile it with your TypeScript build, and test both accepted and rejected production cases against it before you rely on it for runtime validation.

How Required, Optional, Nullable, and Union Fields Are Inferred

The mapping from observed JSON to documented Zod constructors is fixed and minimal. Strings map to z.string, finite JSON numbers to z.number, Booleans to z.boolean, and null to z.null. The table below summarizes how the converter treats each structural case it can encounter.

Sample observationInferred Zod expressionWhy
Object with the same keys in every examplez.object({ ... }), all properties requiredEvery sampled object had the key, so presence is observed as required.
Object where one key is absent in at least one sampleThat property wrapped in .optional(), with recursively merged value typesMissing in some samples means presence is not guaranteed by the sample alone.
Property whose values include null alongside another typeCombined type made .nullable()Null is treated as an explicit nullability marker rather than a separate union member.
Homogeneous array (every element the same shape)Single element schema wrapped in z.array(...)One observed shape is repeated, so one schema fits every position.
Mixed primitive or structural arrayDeterministic z.union([...])Different element shapes were observed and are combined in a stable order.
Empty arrayz.array(z.unknown())No element evidence is available, so the converter refuses to guess.
Nested arrays or nested objectsThe same rules applied recursivelyInference descends into every level up to the documented nesting limit.
Array of objectsElement keys merged across all sampled objectsMerging produces the union of observed keys, with optional markers from above.

Mixed values are ordered deterministically, and identical schemas inside the output are deduplicated, so converting the same JSON twice produces stable source you can diff cleanly in version control.

What the Converter Deliberately Does Not Infer

The generator refuses to guess at any constraint that a single sample cannot prove. It will not infer string formats, minimums, maximums, integer constraints, enums, literals, discriminated unions, dates, UUIDs, URLs, email addresses, brands, coercions, defaults, transformations, refinements, records, tuples, strict-object behavior, recursive references, or business rules. Spellings and small samples cannot establish intent, so those decisions are left to you. Treat the generated schema as a structured hypothesis and tighten it only against authoritative documentation.

Two practical implications follow. First, numeric ranges need a manual .min(), .max(), or .int() once you know the contract. Second, a field that is semantically required but happens to be missing from your single sample will be marked optional; cross-check against your API spec, fixtures, or a broader corpus before accepting that marker. The official Zod API reference lists every method you can append to refine the output, and the Zod source repository documents the exact behaviour of each version.

Strict Parsing and Hard Limits You Can Hit

The page parses strict JSON before any inference happens, so JavaScript comments, trailing commas, NaN, Infinity, undefined, BigInt syntax, single-quoted strings, and object literals are rejected rather than silently repaired. Numbers have already passed through JSON.parse, so they follow JavaScript number precision; identifiers or currency values that need exact decimal semantics should usually stay as strings. Hitting any limit produces an explicit error rather than a partial schema, so you will never paste an inferred fragment without knowing that the conversion stopped.

LimitBoundaryWhat it caps
Input size500,000 charactersThe JSON sample you paste
Visited values50,000 valuesTotal nodes walked during inference
Nesting depth40 levelsDeepest recursive descent
Generated source1,000,000 charactersLength of the emitted Zod text

Deep or very wide examples can still be poor contract documentation even within those limits, so prefer a small representative fixture and split independent payloads into separate schemas.

Validate the Output Against Your Real Contract

After copying the source, install the Zod version you intend to depend on, format the snippet with your project formatter, compile it, and test it against both happy-path payloads and rejection cases drawn from your real fixtures. If you are used to converting JSON samples into other typed declarations, the same source-first discipline applies — for example, the Convert JSON to Rust Struct With Serde Derives workflow also starts from one sample and treats the result as a reviewable draft rather than a finished contract. The Zod converter follows the same philosophy: the schema tells you what one sample proves, the rest of the validation lives in the rules you add by hand against authoritative documentation.

For a deeper look, see How to Validate JSON Syntax and Pinpoint Errors.