> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dottxt.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Reusability

> Use `$defs` and `$ref` to keep repeated structures consistent across your schema.

When the same structure appears in multiple places, such as billing and shipping addresses, home and work phone numbers, or primary and secondary contacts, copy-pasting the definition is fragile. Change one copy and forget the other, and you have two subtly different schemas that should be identical.

`$defs` and `$ref` solve this. You define the structure once in `$defs` and reference it wherever it's needed. Updates happen in one place, and every reference stays in sync.

## Use case

An order output with `billing_address` and `shipping_address` that must follow exactly the same structure: the same fields, the same constraints, and the same required list.

## Schema pattern

<CodeGroup>
  ```json JSON Schema theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  {
    "type": "object",
    "$defs": {
      "address": {
        "type": "object",
        "properties": {
          "line1": { "type": "string", "minLength": 1, "maxLength": 120 },
          "line2": { "type": "string", "maxLength": 120 },
          "city": { "type": "string", "minLength": 1, "maxLength": 80 },
          "postal_code": { "type": "string", "minLength": 3, "maxLength": 20 },
          "country": { "type": "string", "pattern": "^[A-Z]{2}$" }
        },
        "required": ["line1", "city", "postal_code", "country"],
        "additionalProperties": false
      }
    },
    "properties": {
      "order_id": { "type": "string", "pattern": "^ORD-[0-9]{4,10}$" },
      "billing_address": { "$ref": "#/$defs/address" },
      "shipping_address": { "$ref": "#/$defs/address" }
    },
    "required": ["order_id", "billing_address", "shipping_address"],
    "additionalProperties": false
  }
  ```

  ```python Pydantic theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  from pydantic import BaseModel, ConfigDict, Field

  class Address(BaseModel):
      model_config = ConfigDict(extra="forbid")
      line1: str = Field(..., min_length=1, max_length=120)
      line2: str | None = Field(None, max_length=120)
      city: str = Field(..., min_length=1, max_length=80)
      postal_code: str = Field(..., min_length=3, max_length=20)
      country: str = Field(..., pattern=r"^[A-Z]{2}$")

  class OrderPayload(BaseModel):
      model_config = ConfigDict(extra="forbid")
      order_id: str = Field(..., pattern=r"^ORD-[0-9]{4,10}$")
      billing_address: Address
      shipping_address: Address
  ```

  ```typescript Zod theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  import { z, toJSONSchema } from "zod";

  const addressSchema = z.object({
    line1: z.string().min(1).max(120),
    line2: z.string().max(120).optional(),
    city: z.string().min(1).max(80),
    postal_code: z.string().min(3).max(20),
    country: z.string().regex(/^[A-Z]{2}$/),
  }).strict().meta({ id: "addressSchema" });

  const orderPayloadSchema = z.object({
    order_id: z.string().regex(/^ORD-[0-9]{4,10}$/),
    billing_address: addressSchema,
    shipping_address: addressSchema,
  }).strict();

  console.log(JSON.stringify(toJSONSchema(orderPayloadSchema, {
    reused: "ref",
  }), null, 2));
  ```
</CodeGroup>

## Example output

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "order_id": "ORD-8391",
  "billing_address": {
    "line1": "10 Main St",
    "city": "Austin",
    "postal_code": "78701",
    "country": "US"
  },
  "shipping_address": {
    "line1": "55 River Rd",
    "city": "Austin",
    "postal_code": "78702",
    "country": "US"
  }
}
```

## Why this works

Both `billing_address` and `shipping_address` resolve to the same `$defs/address` definition. If you later need to add a `state` field or change `postal_code` to `zip`, you change it once and both addresses update.

This also improves readability. A schema with ten `$ref` references to a well-named definition is easier to review than ten inlined copies of the same 15-line object. Reviewers can focus on the top-level structure and drill into definitions only when needed.

In Zod 4, reuse in code does not automatically become `$defs` reuse in generated JSON Schema. Pass `reused: "ref"` to `toJSONSchema(...)` to extract repeated schemas into `$defs`, and add metadata like `.meta({ id: "addressSchema" })` if you want a stable definition name instead of an auto-generated one.

## Related docs

* [Composition reference](/json-schema/reference/composition)
* [Object reference](/json-schema/reference/object)
