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</>
</>
</>
</>,
);Takumi has no orphans or widows control. A heading can land alone at the foot of a page. Wrapping the heading and its first paragraph in a break-inside: avoid box prevents 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 "takumi-pdf";
< ="flex flex-col gap-1 text-sm">
{.(() => (
< ={.} ={`#${.}`} ="flex items-baseline gap-2">
<>{.}</>
< ="flex-1 border-b border-dotted border-gray-300" />
< ="targetPageNumber" ="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";
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">
< ="pageNumber" /> / < ="totalPages" />
</>
),
: { : 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
<table> markup is not supported. There is no table layout, so cells stack instead of lining up. Build rows out of flex containers, like the example below.
A header row does not repeat across pages on its own. Split the rows into groups and give each group its own header:
import "takumi-pdf";
< ="flex flex-col">
{(, 24).((, ) => (
< ={} ="flex break-inside-avoid flex-col">
< />
{.(() => (
< ={.} ="flex break-inside-avoid gap-3 pt-2 text-xs">
< ="flex-1">{.}</>
< ="w-[100px] text-right">{.}</>
</>
))}
</>
))}
</>;break-inside-avoid moves a group that no longer fits to the next page, header and all. Pick a group size that underfills a page, or the group stops fitting anywhere and splits again.
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