Reports & statements
Generate PDF reports and statements with chapters, bookmarks, tables of contents, and repeating headers.
Use page breaks for chapters, repeating headers for context, and bookmarks or a table of contents for navigation. The examples below combine these features for reports and account statements.
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" }}>
< ={} ="Revenue by quarter" ={{ : 560, : 360 }} />
<>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."] },
});Use your chapters array of { id, title } entries to link each chapter in the table of contents. <TargetPageNumber /> fills in the page number of each link target:
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
Here, accounts contains your account records, Statement is your JSX template, and save(id, pdf) writes each result to storage. 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 (., );
}The renderer reuses registered fonts between documents. Keep tagged enabled when the output needs a structure tree.
Accessibility
Set tagged: "ua1" to request PDF/UA-1 validation. Supply the document language and title, then review heading order, tables, and chart descriptions. See PDF/A and PDF/UA.
Last updated on