# Section

> The one node that holds other nodes, and the heading its title becomes.

Source: https://docxcelerate.com/docs/nodes/section/

The only construct that nests today. Its title carries into the document outline, so the structure you write is the structure the reader sees.

- **Helpers:** `Section`
- **Node kind:** `section`
- **Category:** Structure
- **Resolves:** Locally
- **Children:** Any node, including other sections. No depth limit.

| Option | Type | What it does |
| --- | --- | --- |
| `id` | `string` | Stable address for the node. Engines target it and build artifacts diff on it, so treat a rename as a breaking change. Optional: a node without one takes an id from where it sits, which is what keeps branches and loops from forcing you to invent names. Two nodes claiming one id is an error rather than a race. |
| `title` _required_ | `string` | The heading printed above the children, and the outline entry. |
| `children` | `Yield` | The children, as JSX children of `<Section>`. Components, elements, arrays and conditionals all count; anything falsy is skipped. |
| `derivers` | `DeriverInvocation[]` | Values the engine computes before the node resolves, written to `derived.*` and readable from a template token. Built with `derive()`. These survive publishing and run per document — use them for anything computed from request data. `useDeriver` runs one during the build instead. |

## Writing one

A section takes its options and its children. Those children are node
components — the same values
you would place at the top level of a document:

```ts
import { section } from "docxcelerate";

export const Opening = section<MemberData>({ id: "opening", title: "Your renewal" }, [
  Greeting,
  PriceChange,
]);
```

In TSX the same call reads as markup:

```tsx
<Section id="opening" title="Your renewal">
  <Greeting />
  <PriceChange />
</Section>
```

Both produce the same tree. [Templates](/docs/essentials/templates/) covers when
each form earns its keep.

## Children can be anything

Paragraphs, images, graphs, a contents marker, or further sections. No other
node has children, so the depth of a document is the depth of its sections.

Moving a node inside a section changes nothing about it. It resolves the same
way at any depth, and its id gains no prefix.

## Titles carry into the outline

`title` is required, and it isn't decoration. It becomes the heading above the
children and the entry a contents list is built from. A group that wants no
heading wants no section; put the nodes in the document directly.

## Variants

### A titled group

Two paragraphs under one heading — the common case.

Source: `src/nodes/section/basic.node.tsx`

```tsx
import { Section } from "docxcelerate/template";
import { PriceChange } from "../paragraph/conditional.node.tsx";
import { Greeting } from "../paragraph/static.node.tsx";

/**
 * A section takes an id, a title and its children. The children are the same
 * components you would place at the top level.
 */
export const Opening: Section = () => (
  <Section id="opening" title="Your renewal">
    <Greeting />
    <PriceChange />
  </Section>
);
```

**What it resolves to**

```json
{
  "id": "opening",
  "kind": "section",
  "title": "Your renewal",
  "children": [
    {
      "id": "greeting",
      "kind": "paragraph",
      "mode": "static",
      "text": "Dear Adaeze Nkemelu,"
    },
    {
      "id": "price-change",
      "kind": "paragraph",
      "mode": "static",
      "text": "Your Peak Anytime membership is rising by 5.1%, from £468 to £492 a year. That is £41 a month from 1 October 2026."
    }
  ]
}
```

### Mixed and nested children

A graph, then a nested section holding a graph and a dynamic paragraph.

Source: `src/nodes/section/nested.node.tsx`

```tsx
import { Section } from "docxcelerate/template";
import { ClassMix } from "../graph/pie.node.tsx";
import { VisitsByMonth } from "../graph/bar.node.tsx";
import { NextSteps } from "../paragraph/dynamic.node.tsx";

/**
 * Children can be of any kind, including another section — the one place a
 * document tree gains depth. The resolved document nests exactly as this reads.
 */
export const YourYear: Section = () => (
  <Section id="your-year" title="Your year here">
    <VisitsByMonth />
    <Section id="activity-mix" title="Where the time went">
      <ClassMix />
      <NextSteps />
    </Section>
  </Section>
);
```

**What it resolves to**

```json
{
  "id": "your-year",
  "kind": "section",
  "title": "Your year here",
  "children": [
    {
      "id": "visits-by-month",
      "kind": "graph",
      "mode": "static",
      "graphType": "bar",
      "title": "Visits to Riverside Leisure Centre",
      "valueAxisTitle": "Visits",
      "data": {
        "categories": [
          "Apr",
          "May",
          "Jun",
          "Jul",
          "Aug",
          "Sep"
        ],
        "series": [
          {
            "label": "Visits",
            "values": [
              11,
              14,
              9,
              16,
              18,
              12
            ]
          }
        ]
      },
      "caption": "Your visits to Riverside Leisure Centre, last six months"
    },
    {
      "id": "activity-mix",
      "kind": "section",
      "title": "Where the time went",
      "children": [
        {
          "id": "class-mix",
          "kind": "graph",
          "mode": "static",
          "graphType": "pie",
          "title": "Share of visits",
          "dataLabels": true,
          "data": {
            "categories": [
              "Swim",
              "Strength",
              "Classes"
            ],
            "series": [
              {
                "label": "Share of visits",
                "values": [
                  42,
                  33,
                  25
                ]
              }
            ]
          },
          "caption": "How you used the centre, by activity"
        },
        {
          "id": "next-steps",
          "kind": "paragraph",
          "mode": "dynamic",
          "text": "Your membership renews automatically on 1 October 2026. Nothing is needed from you unless you want to change plan."
        }
      ]
    }
  ]
}
```

## Notes

- **Depth is in the tree, not in the headings.** Both shipped renderers print
  every section title at one level — `<h2>` in the browser, Heading 1 in the
  DOCX — however deeply it nests. The JSON nests correctly, so this is a
  renderer limitation, but a three-deep document will not look three-deep yet.
- There is no dynamic section. An endpoint fills nodes in; it does not decide
  what the document contains.
- An empty section renders as a lone heading. If the children come from a filter
  that can empty out, check for that before returning the section.
