Skip to main content
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

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