Reports & statements
Long documents with chapters, bookmarks, and running bands.
Reports and account statements are long. They break into chapters, repeat a band on every page, and get navigated rather than read straight through.
Chapters
break-before: page starts a section on a fresh page. break-inside: avoid keeps a figure with its caption:
import { } from "takumi-pdf";
const = await (
<>
< ={{ : "page" }}>
<>Results</>
< ={{ : "avoid" }}>
< ="chart.svg" ="Revenue by quarter" />
<>Revenue by quarter</>
</>
</>
</>,
);widows and orphans keep a paragraph from splitting into lone lines, but they do not tie a heading to the text under it. Wrap the heading and its first paragraph in a break-inside: avoid box for that.
See Pagination for the full set of break properties.
Bookmarks
outline: true turns h1–h6 into PDF bookmarks. A reader opens the sidebar and jumps between chapters. Nesting follows heading depth:
import { } from "takumi-pdf";
const = await (, {
: true,
: "en",
: { : "Annual report 2026", : ["Acme Inc."] },
});A table of contents is the same chapters as links, with the page each one starts on. <TargetPageNumber /> fills the page in:
import { } from "takumi-pdf/primitives";
< ="flex flex-col gap-1 text-sm">
{.(() => (
< ={.} ={`#${.}`} ="flex items-baseline gap-2">
<>{.}</>
< ="flex-1 border-b border-dotted border-gray-300" />
< ="w-8 shrink-0 text-right" />
</>
))}
</>;Each entry stays clickable, so the contents page works on screen as well as in print. Give the number a fixed width, or a two-digit page can rewrap the entry and renumber it. See Table of contents.
Running bands
A report header usually names the document and the period. A footer usually carries the page counter:
import { } from "takumi-pdf";
import { , } from "takumi-pdf/primitives";
const = await (, {
: (
< ="flex w-full justify-between px-12 text-[10px] text-gray-500">
<>Annual report 2026</>
<>Acme Inc.</>
</>
),
: (
< ="flex w-full justify-center text-[10px] text-gray-500">
< /> / < />
</>
),
: { : 64, : 64, : 48, : 48 },
});Bands draw in the margin, so the margin has to be tall enough. Headers & footers shows how to derive the margin from the band.
Long tables
Use <table> markup. Rows split across pages, and column positions stay identical on every page. A <thead> that qualifies repeats at the top of each continuation page:
import "takumi-pdf";
< ="w-full text-xs">
<>
<>
< ="text-left">Item</>
< ="text-right">Amount</>
</>
</>
<>
{.(() => (
< ={.}>
<>{.}</>
< ="text-right">{.}</>
</>
))}
</>
</>;Tagged output maps the markup to Table, TR, TH and TD structure elements, so screen readers navigate by row and column. See Tables for layout coverage and PDF/A for the structure tree.
Batches
Statement runs render the same template thousands of times. Construct one PdfRenderer and keep it. Fonts register once and are reused for every document:
import { } from "takumi-pdf";
import { } from "@takumi-rs/helpers";
const = new ();
const = await (["Inter"]);
for (const of ) {
const = await .(< ={} />, { });
await (., );
}Set tagged: false when nobody reads these with assistive technology. It drops the structure tree and the file gets smaller.
Accessibility
Public bodies often have to publish accessible documents. tagged: "ua1" validates the structure tree against PDF/UA-1 during the render. It builds on the heading outline above. See PDF/A for the rest of its inputs.
Last updated on