Takumi

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:

Start chapters on new pages
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 h1h6 into PDF bookmarks. A reader opens the sidebar and jumps between chapters. Nesting follows heading depth:

Generate chapter bookmarks
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:

Build a table of contents
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:

Repeat a report header
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:

Repeat table headers
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:

Render a batch of statements
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

On this page