A reference collection of schemas for shapes that actually come up — not the vocabulary behind them (see What Is JSON Schema? for that) or the mechanics of how a validator processes them (see JSON Schema Validation Explained for that). Every example here is verified against real data with ajv, the same validator behind JSON Schema Validator.
Nested objects
{
"type": "object",
"properties": {
"user": {
"type": "object",
"properties": {
"name": { "type": "string" },
"address": {
"type": "object",
"properties": {
"city": { "type": "string" },
"zip": { "type": "string", "pattern": "^[0-9]{5}$" }
},
"required": ["city", "zip"]
}
},
"required": ["name", "address"]
}
},
"required": ["user"]
}required applies independently at every level — user is required at the root, and city/zip are required inside address, nested exactly as deep as the object they belong to.
Arrays with constraints
{
"type": "object",
"properties": {
"tags": {
"type": "array",
"items": { "type": "string" },
"minItems": 1,
"uniqueItems": true
}
},
"required": ["tags"]
}{"tags": []} fails minItems, and {"tags": ["js", "js"]} fails uniqueItems — both catch real mistakes (an accidentally-empty array, a copy-paste duplicate) that a bare "type": "array" would let through silently.
Restricting values with enum
{ "enum": ["pending", "active", "done"] }Anything outside the list fails, including a value that's the right type and just misspelled — "actve" is still a string, but it's not one of the three allowed ones.
Custom formats with pattern
When format doesn't cover what you need, a regex does:
{ "type": "string", "pattern": "^#[0-9a-fA-F]{6}$" }Matches a 6-digit hex color like #ff8800, rejects a named color like orange or a 3-digit shorthand like #f80 — pattern is exact, with no built-in shorthand-expansion logic.
Built-in format keywords
{
"type": "object",
"properties": {
"email": { "type": "string", "format": "email" },
"createdAt": { "type": "string", "format": "date-time" }
}
}createdAt has to be a full RFC 3339 timestamp like 2026-08-13T10:00:00Z — a bare date like 2026-08-13 fails date-time specifically (use the separate date format for that). Remember format is only enforced if your validator opts into it — covered in more detail here.
Nullable fields
{ "type": "object", "properties": { "middleName": { "type": ["string", "null"] } } }Both {"middleName": "Marie"} and {"middleName": null} pass; {"middleName": 5} doesn't. Leaving out "null" from the type array is a common cause of "why does this reject a field my API genuinely returns as null" bugs.
Polymorphic shapes with oneOf
For a field that can legitimately be one of several distinct shapes, distinguished by a tag:
{
"oneOf": [
{
"type": "object",
"properties": { "type": { "const": "circle" }, "radius": { "type": "number" } },
"required": ["type", "radius"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": { "const": "rectangle" },
"width": { "type": "number" },
"height": { "type": "number" }
},
"required": ["type", "width", "height"],
"additionalProperties": false
}
]
}{"type": "circle", "radius": 5} and {"type": "rectangle", "width": 2, "height": 3} both pass — each matches exactly one branch. A {"type": "triangle", ...} matches neither and fails. additionalProperties: false on both branches matters here: without it, an object with fields from both shapes could match more than one branch at once, which oneOf (unlike anyOf) treats as a failure too — exactly one match is required, not "at least one."
Tuple validation: Draft-07 vs. 2020-12
Validating a fixed-position array — say, a [name, year] pair — uses different keywords depending on the draft:
// Draft-07
{
"type": "array",
"items": [{ "type": "string" }, { "type": "number" }],
"minItems": 2,
"maxItems": 2
}// 2020-12
{
"type": "array",
"prefixItems": [{ "type": "string" }, { "type": "number" }],
"items": false
}Both accept ["Ada", 1815] and reject [1815, "Ada"] (wrong types in the wrong positions). The 2020-12 version's "items": false is what rejects a third array element — in Draft-07, an items array with no additionalItems: false alongside it would silently allow extra trailing elements instead of rejecting them.
Try it yourself
Paste any of these into JSON Schema Validator along with your own data to see them in action, or generate a starting schema from a sample document with JSON Schema Generator. Both run entirely in your browser.