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

# Pagination

> Declare intent at the block level. The engine cuts pages, controls widows and orphans, and repeats table headers.

Content flows; the engine cuts it into pages, controls widows and orphans, and repeats table headers. You never compute page fits. You declare intent at the block level and let the engine satisfy it.

## The three tools

| Intent                                      | Declaration                                 | Where                                       |
| ------------------------------------------- | ------------------------------------------- | ------------------------------------------- |
| "This section starts on a fresh page"       | `style={{ breakBefore: 'page' }}`           | the section's wrapper element               |
| "This block must not straddle a page break" | `style={{ breakInside: 'avoid' }}`          | signature blocks, totals, callouts, figures |
| "Same on every page"                        | `pageOptions.header` / `pageOptions.footer` | module-level `pageOptions`                  |

```tsx theme={null}
<div tw="flex flex-col" style={{ breakBefore: 'page' }}>
  <h2 tw="text-[18px] font-bold text-slate-900">Terms and Conditions</h2>
  {/* ... */}
</div>
```

Running bands are declared once on `pageOptions` and painted on every page, inside the margin. Page counters (`<PageNumber />`, `<TotalPages />`) only resolve inside those bands:

```tsx theme={null}
footer: (
  <div tw="flex w-full justify-center text-[9px] text-slate-400">
    <span tw="flex">
      Page <PageNumber /> of <TotalPages />
    </span>
  </div>
),
```

Keep bands to one line at 8 to 10px. With explicit margins a too-tall band overlaps content; a margin side set to `'auto'` grows to fit its band but eats the text column on every page.

## What to declare, by document shape

* **Contract or proposal:** `breakBefore: 'page'` on each numbered top-level section; `breakInside: 'avoid'` on the signature block.
* **Invoice:** nothing before the table, let it flow. `breakInside: 'avoid'` on the totals block and the payment-details box so neither strands alone.
* **Report:** `breakBefore: 'page'` on chapters only. Do not force-break every heading. Half-empty pages read worse than a heading at 70% depth, and the engine already keeps a heading with its following lines.

## Caveats

* `breakInside: 'avoid'` on **table rows** is unreliable when the table has a flex ancestor, see [Tables](/authoring/tables#engine-quirks). It is reliable on ordinary block elements.
* A `breakInside: 'avoid'` block taller than one page has to split anyway. Keep protected blocks well under a page.
* The **first** element of the document must not carry `breakBefore: 'page'`, or you ship a blank first page.

## Anti-patterns

* Manually sizing "page" `<div>`s to paper height. That is slide thinking; the output drifts the moment content changes.
* `breakBefore` on every heading. Confetti pagination.
* Spacer stacks (`<div tw="h-[200px]">`) to push content to the next page. Use `breakBefore: 'page'`.
* Wrapping the entire document in one `breakInside: 'avoid'`.
* Hardcoded page numbers (`Page 1 of 4` as literal text), or counters in body content.
