JSON to Zod
Generate Zod schemas and inferred TypeScript types from a JSON sample, with optional, nullable, and union fields detected across array items.
Local workspace
Named projects in IndexedDB · Local only — never synced to our servers. Worksp…
Batch workspace
Format multiple files locally in one run.
TypeScript types disappear at runtime, so an API that changes shape breaks your app silently. A Zod schema checks the data where it enters your code and gives you the TypeScript type from the same definition through z.infer. This generator merges every item of an array to decide what's optional, nullable, or a union, keeps whole numbers as z.number().int(), and reuses one schema for identical nested objects. Strings stay z.string(), so add refinements like .email() or .uuid() where your data has them.
Common errors and fixes
Expected number, received string
Zod 3's message when an API sends "42" where the sample had 42. Use z.coerce.number() if strings are expected, or fix the producer.
Invalid input: expected number, received string
The same type mismatch in Zod 4's wording. The issue's path points at the field to check.
Required
Zod 3's message for a missing key. If the field is legitimately absent sometimes, add a sample without it so it's generated as .optional().
Unrecognized key(s) in object
Strict objects reject keys the sample didn't include. Turn off Strict objects, or add the key to the schema.
Options
| Option | Description |
|---|---|
| Root name | Name of the top-level schema. Nested schemas are named after their JSON keys. |
| Strict objects | Adds .strict(), so parsing fails on keys the sample didn't have instead of silently stripping them. |
| Export inferred types | Adds export type Name = z.infer<typeof NameSchema> for every schema. |
FAQ
What's the difference between optional, nullable, and nullish in Zod?
.optional() accepts undefined, which is what a missing key parses to. .nullable() accepts null. .nullish() accepts both. The generator picks one per field: missing from some samples means optional, a null value means nullable, and both means nullish.
How do I get a TypeScript type from the schema?
Use z.infer<typeof RootSchema>. The generator exports one type per schema, so the schema stays the single source of truth for both validation and types.
Why is a field typed with z.union?
The sample has different types at that key, for example 42 in one array item and "42" in another. Keep the union if both really occur, or fix the producer so the field has one type.
Does the generated schema work with Zod 4?
Yes. It only uses APIs that exist in both Zod 3 and Zod 4, including z.record with an explicit key schema. .strict() still works in Zod 4, although z.strictObject is the newer spelling.
Why is a number z.number().int() instead of z.number()?
Every sample value at that key was a whole number. If any sample is fractional, the field widens to z.number(). JSON can't tell 1.0 from 1, so include a fractional sample when a field can hold decimals.