NV ToolkitFormat · Validate · Convert

Code Generators

JSON to Zod Schema Generator

Paste a JSON sample, get a runtime-validating Zod schema plus its inferred TypeScript type — z.object, z.array and nested schemas built for you.

In short

Generate a Zod schema and inferred TypeScript type from a JSON sample: nested objects, arrays, nullable and optional fields — quicktype-powered, in your browser.

By NaveenKumar T · Updated

  • z.object / z.array / z.union from one sample
  • z.infer<> type alongside each schema
  • Nullable and optional fields inferred

Runs entirely in your browser — nothing you paste or open here is uploaded, logged, or stored.How we handle data →

Example

JSON to Zod: input and output

A user record with a nested preferences object, an array of role strings, a null address field and an ISO date-time — the shapes this page's inference rules are built to handle.

Input · JSON
{
  "id": "usr_8f3",
  "email": "ada@example.com",
  "age": 36,
  "roles": ["admin", "editor"],
  "address": null,
  "createdAt": "2026-09-18T09:30:00Z",
  "preferences": { "newsletter": true, "theme": "dark" }
}
Output · Zod schema
import * as z from "zod";


export const PreferencesSchema = z.object({
    "newsletter": z.boolean(),
    "theme": z.string(),
});
export type Preferences = z.infer<typeof PreferencesSchema>;

export const RootSchema = z.object({
    "id": z.string(),
    "email": z.string(),
    "age": z.number(),
    "roles": z.array(z.string()),
    "address": z.null(),
    "createdAt": z.coerce.date(),
    "preferences": PreferencesSchema,
});
export type Root = z.infer<typeof RootSchema>;

preferences became its own PreferencesSchema referenced from RootSchema, address (sampled only as null) came back as z.null(), createdAt became z.coerce.date() rather than z.string(), and every schema got a matching export type via z.infer.

Why generate Zod instead of an interface

A TypeScript interface disappears at runtime — a Zod schema doesn't

Interfaces are compile-time only: the moment a real HTTP response reaches your code, there's nothing left to check it against. A Zod schema is a value you can call at the boundary — parse() or safeParse() — that validates the payload AND, via z.infer, still gives you the static type for everything downstream.

Validate at the boundary, not by convention

API responses drift — a field goes missing, a number arrives as a string. A Zod schema catches that the moment the payload crosses into your code, instead of an interface silently lying about a shape that no longer matches reality.

One schema, two artifacts

Every generated schema exports a matching type via z.infer<typeof XSchema> — write the validation once and the TypeScript type for the rest of your codebase falls out of it for free, so the two never drift apart.

Nested objects become nested schemas

A nested object in your sample becomes its own named schema constant (PreferencesSchema, CustomerSchema) referenced from the parent — readable, and reusable on its own if a nested shape needs validating independently.

100% client-side, paste real payloads

quicktype runs compiled to JavaScript in your browser — a real (perhaps sensitive) API response never leaves your machine, so you can generate from production data instead of a sanitized stand-in.

Learn more

From JSON sample to Zod schema

The conversion works the same way every generator on this site does — infer a shape from the values in your sample — but targets Zod's schema-builder API instead of a language's type syntax. An object becomes z.object({...}), an array becomes z.array(...) of whatever its elements resolve to, and a nested object gets its own named schema constant (declared before the schema that uses it, since Zod schemas reference each other by value, not by forward declaration the way TypeScript interfaces can).

One inference rule is Zod-specific and worth knowing before you rely on it: a string field whose every sampled value is a full ISO 8601 date-time (2026-09-18T09:30:00Z) is generated as z.coerce.date() rather than z.string() — the coerce variant that accepts a date-time STRING as input and parses it into a real JavaScript Date on the way out, which is usually what you want at an API boundary where dates always arrive as JSON strings.

JSON value → Zod
JSON valueGenerated Zod
Objectz.object({ ... })
Arrayz.array(elementSchema)
Stringz.string()
ISO date-time stringz.coerce.date()
Numberz.number()
Booleanz.boolean()
null only (every sample)z.null()
null in one sample, a value in anotherz.union([z.null(), valueSchema])
Present in one sample, missing in anothervalueSchema.optional()
Nested objectA separate, named XSchema constant

Zod vs JSON Schema vs TypeScript interfaces

All three describe the shape of a JSON value, and this site can generate any of them from the same sample — the difference is where and how each one is enforced. A TypeScript interface is compile-time only and free at runtime, but proves nothing once real data arrives. A JSON Schema is a portable, language-agnostic document you can hand to any conforming validator (JSON Schema Validator included) in any language, but it's data describing a shape, not executable validation code in your own codebase. A Zod schema is executable TypeScript that both validates AND yields a static type via z.infer — the tightest coupling of the three, and the right choice specifically when the validating code and the consuming code live in the same TypeScript project.

Zod vs JSON Schema vs TypeScript interfaces
ArtifactRuns atGives you a static type?Portable across languages?
TypeScript interfaceCompile time onlyYes (that's all it is)No — TypeScript only
JSON SchemaWherever a validator runs itNo, by itselfYes — any conforming validator
Zod schemaRuntime, inside your TS codeYes, via z.inferNo — TypeScript/JavaScript only

Using the generated schema

Drop the generated file into your project and call RootSchema.parse(data) at the boundary where untrusted data enters — the top of an API route handler, right after a fetch() resolves, or the first line inside a webhook handler. parse throws a ZodError (with a field-by-field breakdown of what failed) on invalid input; safeParse returns a { success, data } or { success: false, error } result instead, for call sites that would rather branch than catch. Either way, once validation succeeds, the value is typed as Root for everything downstream — no separate assertion or cast needed, because z.infer already tied the runtime check and the static type together.

typescript
import { RootSchema, type Root } from "./schema";

// Throws a ZodError with a field-level report on a bad payload.
const user: Root = RootSchema.parse(await response.json());

// Or, to branch instead of catching:
const result = RootSchema.safeParse(await response.json());
if (!result.success) {
  console.error(result.error.flatten());
} else {
  console.log(result.data.email); // typed as Root
}

Help

Frequently asked questions

Everything you need to know about the JSON to Zod.

01Why generate Zod instead of TypeScript interfaces?

Interfaces are erased when TypeScript compiles to JavaScript, so they can describe a shape but never check one — the instant a real payload arrives from the network, there's nothing left at runtime to compare it against. A Zod schema is an ordinary JavaScript value that exists at runtime: call RootSchema.parse(data) and it either returns a value TypeScript now knows is genuinely shaped like Root, or throws with a precise, field-level error describing exactly what didn't match. z.infer<typeof RootSchema> then hands you back the exact same TypeScript type an interface would have given you, so you keep the compile-time benefit while adding the runtime one. If you only need the compile-time type — the payload is trusted, or checked elsewhere — [JSON to TypeScript](/json-to-typescript) is the lighter-weight page for that.

02How are null and missing fields represented?

This is verified against the real generator output, not assumed: a field whose only sampled value is null becomes z.null() — an honest but not very useful type, since it can only ever validate null itself. Paste (or merge) a second sample where that field carries a real value and the schema widens correctly to a union, z.union([z.null(), z.string()]) — genuinely nullable, not just null. A field that's present in one sample and absent from another becomes z.string().optional() instead, because quicktype tracks "sometimes missing" and "sometimes null" as different situations, which is also how Zod itself distinguishes an absent key from an explicit null.

03Which Zod version does the output target?

The generated import line, import * as z from "zod", is valid against both Zod 3 and Zod 4's published APIs — the object/array/union/optional/nullable primitives used in the generated schema have been stable across that boundary. Nothing library-version-specific is emitted (no Zod-4-only error customization APIs, for instance), so the output should drop into either major version without edits; if your project pins Zod 3 specifically, double-check any project-wide import convention (some codebases import { z } from "zod" instead) and adjust that one line to match your team's style.

04What does the Schema-only switch drop?

Only the export type X = z.infer<typeof XSchema>; lines — every schema constant (RootSchema, and any nested XSchema constants) stays exactly as-is. Use Schema-only when a separate part of your codebase already declares the TypeScript types by hand and you only want the runtime validator, or when you're going to derive types some other way and don't want the generated file exporting names that might collide.

05Can I get constraints like min/max or email()?

No — and this is inherent to generating from a SAMPLE rather than a specification: a sample value like "ada@example.com" tells the generator the field is a string, but nothing in that one value says every value in that field must look like an email address, or that a string field has a maximum length. Add .email(), .min(1), .max(140) and similar refinements to the generated schema by hand once you know the real constraints. If you're starting from a contract that already states its constraints (an OpenAPI spec, a hand-written JSON Schema), [JSON Schema Validator](/json-schema-validator) can enforce those directly, and [JSON to JSON Schema](/json-to-json-schema) generates that intermediate contract from a sample the same way this page generates Zod.

Keep working

Related tools