> ## 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.

# Recursion

> Model tree-like outputs where nodes contain children of the same shape.

Some data is naturally tree-shaped: file systems, navigation menus, org charts, comment threads, document outlines. A recursive schema models this by letting a node's `children` field reference the same node definition, creating an arbitrarily nested structure from a single type.

The key concern with recursive schemas is unbounded growth. In this pattern, `maxItems` on the `children` array does not limit nesting depth directly; it limits how many children each node can have, which keeps deep trees from exploding in size. If you need a hard depth limit, you need a different schema design.

## Use case

Generating a documentation table of contents tree from markdown headings, where each section can contain subsections of the same shape.

## Schema pattern

<CodeGroup>
  ```json JSON Schema theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  {
    "$defs": {
      "tocNode": {
        "type": "object",
        "properties": {
          "title": { "type": "string", "minLength": 1, "maxLength": 120 },
          "slug": { "type": "string", "pattern": "^[a-z0-9-]+$" },
          "children": {
            "type": "array",
            "items": { "$ref": "#/$defs/tocNode" },
            "maxItems": 20
          }
        },
        "required": ["title", "slug", "children"],
        "additionalProperties": false
      }
    },
    "type": "object",
    "properties": {
      "root": { "$ref": "#/$defs/tocNode" }
    },
    "required": ["root"],
    "additionalProperties": false
  }
  ```

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

  class TocNode(BaseModel):
      model_config = ConfigDict(extra="forbid")
      title: str = Field(..., min_length=1, max_length=120)
      slug: str = Field(..., pattern=r"^[a-z0-9-]+$")
      children: list[TocNode] = Field(..., max_length=20)

  class TocPayload(BaseModel):
      model_config = ConfigDict(extra="forbid")
      root: TocNode

  TocPayload.model_rebuild()
  ```

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

  type TocNode = {
    title: string;
    slug: string;
    children: TocNode[];
  };

  const tocNodeSchema: z.ZodType<TocNode> = z.object({
    title: z.string().min(1).max(120),
    slug: z.string().regex(/^[a-z0-9-]+$/),
    children: z.array(z.lazy(() => tocNodeSchema)).max(20),
  }).strict();

  const tocPayloadSchema = z.object({ root: tocNodeSchema }).strict();
  ```
</CodeGroup>

## Example output

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "root": {
    "title": "Getting Started",
    "slug": "getting-started",
    "children": [
      {
        "title": "Installation",
        "slug": "installation",
        "children": []
      },
      {
        "title": "Quickstart",
        "slug": "quickstart",
        "children": [
          {
            "title": "First Request",
            "slug": "first-request",
            "children": []
          }
        ]
      }
    ]
  }
}
```

## Why this works

The `$ref: "#/$defs/tocNode"` inside the `children` array creates the recursion: each node can contain children of the same shape, to any depth. Every node at every level is validated against the same constraints: `title` is bounded, `slug` matches a URL-safe pattern, and `children` exists (even if empty).

`maxItems: 20` on `children` prevents any single node from having too many children. It does not cap recursion depth, but it does cap branching at each level, which keeps the total tree size more manageable and rendering more predictable. Without this bound, the model might generate hundreds of leaf nodes under a single parent, overwhelming your UI or exceeding token limits.

## Related docs

* [Composition reference](/json-schema/reference/composition)
* [Bounded Arrays cookbook](/json-schema/bounded-arrays)
