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
Example output
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.