Reusable Schemas with $ref and $defs
Use $ref and $defs in JSON Schema to create reusable type definitions. Define an address or line item once and reference it across multiple properties.
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"billing": { "$ref": "#/$defs/Address" },
"shipping": { "$ref": "#/$defs/Address" },
"items": {
"type": "array",
"items": { "$ref": "#/$defs/LineItem" },
"minItems": 1
}
},
"required": ["billing", "shipping", "items"],
"$defs": {
"Address": {
"type": "object",
"properties": {
"street": { "type": "string", "minLength": 1 },
"city": { "type": "string", "minLength": 1 },
"state": { "type": "string", "minLength": 1 },
"postalCode": { "type": "string", "pattern": "^\\d{5}(-\\d{4})?$" },
"country": { "type": "string", "minLength": 2, "maxLength": 2 }
},
"required": ["street", "city", "country"]
},
"LineItem": {
"type": "object",
"properties": {
"productId": { "type": "integer", "minimum": 1 },
"name": { "type": "string", "minLength": 1 },
"quantity": { "type": "integer", "minimum": 1, "maximum": 999 },
"unitPrice": { "type": "number", "minimum": 0, "multipleOf": 0.01 }
},
"required": ["productId", "name", "quantity", "unitPrice"]
}
}
}Test Data
{
"billing": {
"street": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postalCode": "94102",
"country": "US"
},
"shipping": {
"street": "456 Oak Ave",
"city": "Los Angeles",
"state": "CA",
"postalCode": "90001",
"country": "US"
},
"items": [
{
"productId": 1001,
"name": "Wireless Keyboard",
"quantity": 2,
"unitPrice": 49.99
},
{
"productId": 2005,
"name": "USB-C Cable",
"quantity": 5,
"unitPrice": 12.99
}
]
}Detailed Explanation
Reusable Schemas with $ref and $defs
As schemas grow, you inevitably need the same structure in multiple places — an address used for both billing and shipping, or a line item used in orders and invoices. The $ref keyword lets you reference a schema defined elsewhere, and $defs provides a standard location for those definitions.
Basic Syntax
{
"$defs": {
"MyType": {
"type": "string",
"minLength": 1
}
},
"properties": {
"name": { "$ref": "#/$defs/MyType" }
}
}
The $ref value is a JSON Pointer — #/$defs/MyType means "look in the current document, under $defs, at the key MyType."
How the Example Schema Works
The schema models an e-commerce order with two reusable definitions:
Address — defines a postal address with street, city, state, postalCode, and country. It is referenced twice: once for billing and once for shipping. Both properties share the exact same validation rules — any change to the Address definition automatically applies to both.
LineItem — defines a product line item with productId, name, quantity, and unitPrice. It is used as the items schema for the order's items array.
The test data provides valid billing and shipping addresses (both conforming to the Address schema) and two valid line items (each conforming to LineItem).
$defs vs. definitions
- Draft 2019-09+: Use
$defs(with a dollar sign). - Draft 7 and earlier: Use
definitions(without a dollar sign).
Both work the same way — the rename was part of a broader effort to prefix JSON Schema keywords with $. Most modern validators support both.
External References
$ref can also point to external files:
{ "$ref": "./address.json" }
{ "$ref": "https://api.example.com/schemas/address.json" }
External references are resolved relative to the schema's URI. This enables a library of shared schemas across multiple API endpoints.
Overriding $ref Properties
In Draft 2019-09+, you can place keywords alongside $ref:
{
"$ref": "#/$defs/Address",
"required": ["street", "city", "state", "postalCode", "country"]
}
This references the Address definition but adds a stricter required list. In older drafts, all siblings of $ref were ignored — be cautious with backward compatibility.
Best Practices
- Name definitions clearly: Use PascalCase names that match your domain model (
Address,LineItem,User). - Keep $defs at the root: Place all definitions at the document root for discoverability.
- Avoid circular references: While technically allowed, circular
$refchains can cause infinite loops in some validators.
Use Case
Use $ref and $defs in any schema with repeated structures: orders with billing and shipping addresses, APIs with paginated list responses sharing a common envelope, microservices sharing domain models, and OpenAPI specifications where the components/schemas section holds all reusable types.