100% local — your data never leaves your browser

JSON to Zod — Validate at the Boundary

Turn a JSON sample into Zod schemas. One schema per object, declared before use, and z.infer gives you the TypeScript type from the same source.

Instant Private Zero cookies

JSON input

Zod output

What this tool does

A JSON sample becomes Zod schemas, one const per object.

{ "id": 1, "name": "Ada", "address": { "city": "Paris" } }
import { z } from "zod";

export const AddressSchema = z.object({
  city: z.string(),
});

export const RootSchema = z.object({
  id: z.int(),
  name: z.string(),
  address: AddressSchema,
});

Children come first: these are runtime constants, and a schema has to exist before another one mentions it.

Numbers are read as they were written

z.int() for an integer, z.number() for anything written with a decimal point or an exponent:

{ "price": 10.0, "quantity": 3 }
price: z.number(),
quantity: z.int(),

Until recently price came out z.int(), because 10.0 parses to a whole number and nothing kept the literal. The schema then rejected 10.5 — the next record, most likely. JSON has one number type, so the written form is the only evidence of what the producer meant; the conversion now reads it. This was found while writing this page and fixed.

Zod 4 syntax

z.int() is the Zod 4 spelling. On Zod 3, write z.number().int(); nothing else in the output differs between the two versions.

What a sample cannot decide for you

  • Nothing is .optional(). A key seen once can appear; whether it always does is not in the document.
  • null gives z.null(), a schema that accepts nothing else. If the field is “a string, sometimes absent”, .nullable() is yours to add.
  • Unknown keys are stripped, not refused, because that is Zod’s default. .strict() is a decision about your API.
  • A mixed array becomes a union: [1, "a"] gives z.array(z.union([z.int(), z.string()])), which describes the sample exactly and may well be wider than your real data.

Each of these is a place where the generated schema is a starting point, and the page says so rather than letting you find out in review.

Private by design

Everything runs locally in your browser with JavaScript. Your data is never uploaded, which makes the tool safe for sensitive content, and it keeps working offline.

Frequently asked questions

Why is `10.0` a `z.number()` but `3` a `z.int()`?
Because that is how they were written. JSON has one number type, so the only clue about the original is the literal: a decimal point or an exponent means the producer had a float. `10.0` used to come out `z.int()`, and the schema then rejected 10.5 — the very next record. That was a real defect, fixed.
Is `z.int()` valid in Zod 3?
No, it arrived in Zod 4. On version 3 the same thing is written `z.number().int()`, and a search-and-replace is enough. Everything else in the output — `z.object`, `z.array`, `z.union`, `z.null` — is unchanged between the two versions.
Does the schema reject unknown keys?
No. `z.object()` strips what it does not know rather than failing, which is Zod\u2019s default and stays that way here. If you want an extra key to be an error, add `.strict()` yourself — it is a decision about your API, not something a sample can tell us.

Related converters