Skip to content
Tools

JSON to Zod Schema Generator

Last updated:

This tool reads a JSON sample and writes the matching Zod schema, using z.object, z.array, z.string and friends, plus a z.infer type alias. You get runtime validation and a static TypeScript type from one definition.

Runs in your browser: your sample JSON never leaves your device – nothing is uploaded, logged or stored on a server. How we protect your data

100% in-browserNothing is uploaded

Input

Loading editor…

Paste a JSON sample

or drop a file anywhere in this box. Everything stays in your browser.

Drop a file to load it

Output

Loading editor…

Zod code appears here

Results update as you type.

How to use the JSON to Zod

  1. Paste a JSON object or array that represents the data you will receive.
  2. Set the schema name; the generated type uses the same name without the Schema suffix.
  3. Tighten the output by hand where you know more than the sample (min lengths, enums, .int()).
  4. Copy the code and call RootSchema.parse(data) or .safeParse(data) where the data enters your app.

Worked example

A task object with a nullable due date

Input

{"id":42,"title":"Ship v2","done":false,"tags":["release"],"due":null}

Zod

import { z } from "zod";

const RootSchema = z.object({
  id: z.number().int(),
  title: z.string(),
  done: z.boolean(),
  tags: z.array(z.string()),
  due: z.null(),
});

type Root = z.infer<typeof RootSchema>;

due is null in the only sample, so the schema can only say z.null(). If it is really a date string or null, change it to z.string().nullable().

Refinements to add after generating

A sample shows types, not rules. These common edits turn a generated schema into a useful contract.

Refinements to add after generating
You know that…ChangeTo
id is always a whole numberz.number()z.number().int()
status is one of a fixed setz.string()z.enum(["open", "closed"])
a field can be absentz.string()z.string().optional()
a field can be nullz.null()z.string().nullable()
email must look like an emailz.string()z.string().email()
unknown keys should be rejectedz.object({...})z.object({...}).strict()

What happens to unknown keys

By default z.object strips keys that are not in the schema: parse succeeds and the extra fields are silently dropped from the result. Use .strict() to reject them, or .passthrough() to keep them. For API responses, stripping is usually what you want; for config files, strict catches typos.

parse vs. safeParse

parse throws a ZodError on invalid input, which suits code paths where bad data is a bug. safeParse returns { success, data } or { success, error } and never throws, which suits form input and third-party APIs. Both give you a typed result on success.

JSON to Zod FAQ

Why use Zod instead of a TypeScript interface?

An interface is erased at compile time and cannot check data. A Zod schema runs at runtime, rejects bad input with readable errors, and gives you the same static type through z.infer.

How do I make a Zod field optional?

Append .optional() to the field's schema, e.g. email: z.string().optional(). For fields that can be present but null, use .nullable(); for both, .nullish().

Does Zod coerce strings to numbers?

Not by default: z.number() rejects "42". Use z.coerce.number() when the input is form data or query strings where everything arrives as text.

Can I convert a Zod schema to JSON Schema?

Yes. Recent Zod versions include a toJSONSchema helper, and the zod-to-json-schema package covers older versions. Generating JSON Schema directly from your sample is also possible with our generator.

Will the generated schema validate other responses from the same API?

Only if they have the same shape as your sample. Fields missing from the sample will be stripped, and fields that vary in type will fail. Test the schema against a few real responses before shipping.

Related tools