JSON to Zod Schema

Walks a JSON sample's shape and emits Zod schema-builder code (z.object/z.array/z.string/...), with .nullable() for fields observed as null, .optional() for fields missing from some array elements, and a bonus type export via z.infer. A free online tool from Staaarter, right in your browser.

Runs locallyUpdated 2026-07-26

Overview

Introduction

Hand-writing a Zod schema for a JSON payload means transcribing every field's type, and remembering which fields need `.nullable()` or `.optional()`, by eye. This tool infers all of that directly from a real JSON sample and emits ready-to-use Zod schema-builder code.

It's one of four sibling generators on this site (TypeScript, Python, Go, Zod) that share the same shape-inference logic and differ only in how that shape is rendered as target-language code; this one is the only one of the four that produces a runtime validator rather than a compile-time-only type.

What Is JSON to Zod Schema?

A generator that infers a Zod schema from a JSON sample and emits schema-builder code: `z.string()`, `z.number()`, `z.boolean()` for primitives, `z.array(T)` for arrays, and `z.object({...})` for objects, with nested objects inlined directly (rather than hoisted into separate named consts, matching how Zod schemas are conventionally hand-written).

Fields observed as `null` get `.nullable()` appended; fields missing from some elements of a merged array of objects get `.optional()` appended; a field can get both. The output ends with a top-level `const <Name>Schema = z.object({...});` plus a `type <Name> = z.infer<typeof <Name>Schema>;` line deriving a matching TypeScript type directly from the schema.

The output is plain text Zod code; the `zod` package itself is never imported or executed by this tool, only referenced in the generated source for you to use in a project that has it installed.

How JSON to Zod Schema Works

The generator parses your JSON, then walks it via the same shared shape-inference step used by this site's TypeScript, Python, and Go generators, classifying every value (string, integer, float, boolean, null, an array, or an object with named fields, tracking per-field nullability and, for arrays of objects, optionality).

Rendering that shape as Zod recurses directly into properly indented, nested `z.object({...})` calls without any hoisting step, appending `.nullable()`/`.optional()` per field as needed, then wraps the whole result in a top-level `const <rootName>Schema = ...;` declaration followed by the `z.infer` type export line.

When To Use JSON to Zod Schema

Use it right after capturing a real API response or config sample, when you want a runtime validator for that shape in a TypeScript/JavaScript project without hand-writing `z.object({...})` and tracking nullability/optionality by eye.

It's a natural pairing with the JSON to TypeScript Interface tool: generate a Zod schema for the runtime validation boundary, and let the schema's own `z.infer` type export be the single source of truth for the corresponding compile-time type, instead of maintaining a hand-written interface separately.

Features

Advantages

  • Produces a real runtime validator, unlike a compile-time-only TypeScript interface, which can catch a malformed payload at the actual point it enters your code.
  • Includes a bonus `z.infer` type export, giving you a matching compile-time type derived from the same schema instead of two separately maintained sources of truth.
  • Correctly applies `.nullable()` and `.optional()` based on values actually observed across a merged array of objects, not just the first element.
  • Shares its shape-inference behavior with this site's TypeScript, Python, and Go generators, so field-level nullability/optionality decisions are consistent across all four output languages.

Limitations

  • Generates Zod code as text only; it doesn't run or import the zod package, so it can't confirm the output actually compiles against your project's installed Zod version.
  • Type inference is purely sample-based: a field that's always a string in your sample but sometimes a number in real data won't be reflected as a union unless your sample shows both.
  • Doesn't infer Zod's richer refinements (`.min()`, `.max()`, `.email()`, `.uuid()`, etc.); every string becomes a plain `z.string()` and every number a plain `z.number()`, since a single JSON sample gives no evidence of those constraints.
  • Nested objects are always inlined, never hoisted into their own named consts; deeply nested schemas can produce a large single block that's worth manually splitting up for readability in a real codebase.

Examples

Flat object

Input

{"id":1,"name":"Ada","active":true}

Output

const RootObjectSchema = z.object({
  id: z.number(),
  name: z.string(),
  active: z.boolean(),
});

type RootObject = z.infer<typeof RootObjectSchema>;

Each key gets its inferred Zod schema type, and the bonus type export derives a matching TypeScript type via z.infer.

Nested object inlined

Input

{"address":{"city":"London"}}

Output

const RootObjectSchema = z.object({
  address: z.object({
    city: z.string(),
  }),
});

type RootObject = z.infer<typeof RootObjectSchema>;

The nested address object is written inline as its own z.object({...}) rather than hoisted into a separate const.

Nullable field from a merged array

Input

[{"a":1},{"a":null}]

Output

const RootObjectSchema = z.object({
  a: z.number().nullable(),
});

type RootObject = z.infer<typeof RootObjectSchema>;

a is present in every element but observed as null once, so .nullable() is appended to its schema.

Best Practices & Notes

Best Practices

  • Add refinements (.min(), .max(), .email(), .regex(), etc.) by hand for anything user-facing; a single sample can't tell this generator those constraints exist.
  • Use the bonus z.infer type export as your project's source of truth for the corresponding TypeScript type, instead of maintaining a separately hand-written interface that can drift out of sync.
  • Extract a deeply nested inline z.object({...}) into its own named const by hand if it's reused elsewhere or the generated schema gets hard to read.
  • Confirm the generated code actually compiles in your project once pasted in, since this tool never imports or executes zod itself.

Developer Notes

Built on a shared shape-inference module (also used by this site's TypeScript, Python, and Go generators), but unlike those three renderers, this one has no hoisting/name-reservation step at all: object shapes are rendered recursively inline, with indentation tracked by nesting depth (2 spaces per level) rather than via a declarations map. `.nullable()` and `.optional()` are appended as independent, composable modifiers in that fixed order, matching Zod's own chainable API, and can both appear on the same field.

JSON to Zod Schema Use Cases

  • Adding runtime validation at an API boundary (a request body, a webhook payload, a third-party API response) in a TypeScript/JavaScript project.
  • Getting both a runtime schema and a matching compile-time type from one generation step instead of maintaining them separately.
  • Bootstrapping form-validation schemas from a representative sample of the data a form is expected to produce.
  • Documenting a JSON payload's shape for a TypeScript codebase in a form that's also directly usable, runnable validation code.

Common Mistakes

  • Treating the generated schema as complete validation without adding refinements (string formats, numeric bounds, enums) that a single sample can't reveal on its own.
  • Assuming the type export means the schema was actually checked against the zod package; this tool only emits text and never imports or runs zod.
  • Forgetting that .nullable() and .optional() mean different things in Zod (null allowed vs. key can be missing) and mixing them up when hand-editing the output.

Tips

  • Pair with the JSON to TypeScript Interface tool when you want to compare a compile-time-only type against a runtime-validated one for the same sample.
  • Search the output for .nullable() and .optional() to quickly see which fields your sample showed as inconsistent, worth double-checking against your real API contract.
  • For a schema you'll reuse across multiple files, move the generated const into its own module and export it, rather than pasting the definition inline everywhere it's used.

References

Frequently Asked Questions