The required keyword in JSON Schema is one of the most-used keywords in the spec — and one of the most misunderstood. Developers reach for it expecting "this field must have a real value", then are surprised when validation passes for objects containing "", null, or 0.
This post explains exactly what required does, what it doesn't do, and how to combine it with other keywords to actually enforce non-empty values. The behavior described here follows JSON Schema Draft 2020-12, the current specification.
What required actually does
required is an array of property names placed at the object level. A document is valid only if every name in that array is present as a property key in the object.
{
"type": "object",
"required": ["name", "email"],
"properties": {
"name": { "type": "string" },
"email": { "type": "string" }
}
}
This schema rejects {"name": "Alice"} (missing email) and rejects {"email": "a@b.com"} (missing name). It accepts {"name": "Alice", "email": "a@b.com"}.
What required does NOT do
It does not look inside the value. As long as the key exists, the value can be anything that the property's own sub-schema allows. All of these documents pass the schema above:
{ "name": "", "email": "" }
{ "name": " ", "email": " " }
{ "name": "Alice", "email": "not-an-email" }
And these documents pass when the sub-schema permits null or other types:
// Schema where type allows null
{ "name": null, "email": null }
// Schema with no type restriction
{ "name": 0, "email": false }
The key was present in every case. That is the entire contract of required.
How to actually enforce non-empty values
Combine required with the sub-schema's own rules. Three patterns cover almost every case:
1. Non-empty string
{
"type": "object",
"required": ["name"],
"properties": {
"name": { "type": "string", "minLength": 1 }
}
}
Now {"name": ""} fails with two helpful pieces of information: the path (/name) and the keyword (minLength).
2. Non-blank string (whitespace-only is rejected)
minLength: 1 still accepts " ". To reject whitespace-only, use a regex:
{
"type": "string",
"pattern": "\\S"
}
\S means "any non-whitespace character" — the value must contain at least one of them.
3. Forbid null on a required field
If the sub-schema's type permits null (either explicitly or via the absence of a type restriction), required won't reject null. To forbid it, narrow the type:
{
"type": "object",
"required": ["userId"],
"properties": {
"userId": { "type": "string" } // does NOT allow null
}
}
Why required lives at the object level, not inside each property
Beginners often try to write something like:
// This does NOT work — required is not a keyword on individual properties
{
"properties": {
"name": { "type": "string", "required": true }
}
}
The reason: a sub-schema describes a single value in isolation. It has no way to know whether its key is mandatory because the same sub-schema could be reused in many places where it's required in some and optional in others. The decision belongs to the parent object that owns the property.
Required vs optional vs nullable — three different ideas
These three concepts are independent and combine in eight different ways:
| Required? | Allows null? | Behaviour |
|---|---|---|
| Yes | No | Property must be present and must not be null. Most common. |
| Yes | Yes | Property must be present but can be null. Useful for "explicitly unset" semantics. |
| No | No | Property may be absent; if present, must not be null. Common for optional config. |
| No | Yes | Property may be absent or null. Maximum flexibility, minimum guarantees. |
Required vs optional is controlled by the parent object's required array. Allowing null is controlled by the property's type (e.g., ["string", "null"]).
Conditionally required fields
Sometimes a field is required only when another field has a specific value. Use oneOf with discriminators, or if/then/else:
With oneOf
{
"oneOf": [
{
"type": "object",
"required": ["kind", "last4"],
"properties": {
"kind": { "const": "card" },
"last4": { "type": "string" }
}
},
{
"type": "object",
"required": ["kind", "iban"],
"properties": {
"kind": { "const": "bank" },
"iban": { "type": "string" }
}
}
]
}
A card payment requires last4; a bank payment requires iban. Neither is required when the other is selected.
With if/then
{
"type": "object",
"required": ["kind"],
"properties": {
"kind": { "enum": ["card", "bank"] },
"last4": { "type": "string" },
"iban": { "type": "string" }
},
"if": { "properties": { "kind": { "const": "card" } } },
"then": { "required": ["last4"] },
"else": { "required": ["iban"] }
}
How required errors look
When a required field is missing, a validator produces an error like this:
Path: / Message: must have required property 'email' (missing: email) Keyword: required
The path is the parent object that owns the missing property (/ for the root, /users/0 for a nested case). The keyword is always required. The detail in parentheses tells you exactly which property is missing — useful when several are required in the same array.
Try it on real schemas
Open the JSON Schema Validator, load one of the six built-in samples, and add or remove a name from the required array to see the error firsthand.
Does JSON Schema validate required field values or just their presence?
The required keyword only validates presence — it checks that a property key exists in the object. It does not validate the value at all. A property listed in required passes validation even if its value is an empty string (""), null, 0, or false. To validate the value, you must combine required with additional keywords: use minLength: 1 to reject empty strings, exclude null using a type array that omits "null", or use minimum to enforce a numeric range.
Frequently Asked Questions
Why is my required field accepting an empty string?
Because required only checks that the property is present, not that its value is non-empty. An empty string, null, or zero all satisfy required. To enforce a non-empty value, combine required with minLength (for strings) or with a type restriction that excludes null.
What is the difference between required and optional in JSON Schema?
Properties are optional by default. To make one or more properties required, list their names in the required array at the object level. There is no opposite keyword called optional — anything not in the required array is automatically optional.
Why is required listed at the object level instead of inside each property?
Because required is a property of the parent object, not of the property itself. Each sub-schema describes one value in isolation, so it has no way to know whether its key is mandatory. The parent object decides which of its children are required.
Can I make a field required in some cases but not others?
Yes — use oneOf, anyOf, or if/then/else to express conditional requirements. For example, you can require an iban field only when the kind discriminator equals "bank", and require a card_number field only when kind equals "card".
Does required care about the order of property names?
No. ["name", "email"] and ["email", "name"] are equivalent. The array is treated as a set.