Takumi

Invoices

Generate paginated PDF invoices with line items, totals, page numbers, and optional embedded XML.

Render line items and totals as a paged document. If your invoice format needs embedded XML, attach it and set the PDF/A and XMP options the format requires.

A plain invoice

Here, invoice is your invoice data and InvoiceDocument is your JSX template. Line items flow across pages, and the footer carries the page counter. measure() returns the footer's height, so you can reserve enough bottom margin:

Reserve space for an invoice footer
import {  } from "@takumi-rs/helpers";
import { ,  } from "takumi-pdf";
import { ,  } from "takumi-pdf/primitives";

const  = (
  < ="flex w-full justify-between px-12 text-[10px] text-[#6b7280]">
    <>Invoice {.}</>
    < ="flex">
      Page < /> of < />
    </>
  </>
);

const  = await (["Inter"]);
const {  } = await (, { : "a4",  });

const  = await (< ={} />, {
  : "a4",
  : { : 48, :  + 32, : 48, : 48 },
  ,
  ,
});

For complete templates, see the invoice and receipt example.

Use break-inside: avoid, or break-inside-avoid in Tailwind, to keep a line item on one page:

Keep a line item together
import "takumi-pdf";

< ="flex break-inside-avoid gap-3 pt-3 text-xs">
  < ="flex-1">{.}</>
  < ="w-[100px] text-right">{.}</>
</>;

Apply the same style to the totals block.

Electronic invoices

Factur-X and ZUGFeRD combine a readable PDF invoice with structured XML. Map what your profile requires to these options:

The profile requiresThe option
A PDF/A-3 containerpdfa: "3b"
The XML attached by nameattachments
An fx: XMP blockmetadata.xmp
A modification date per filemodificationDate
Embedded subset fontsalways on

Each attachment's modificationDate falls back to metadata.creationDate, so one document date covers both. This example also validates against PDF/UA-1:

Attach invoice XML
import {  } from "takumi-pdf";

const  = await (, {
  : "en",
  : "3b",
  : "ua1",
  : {
    : "Invoice INV-2026-0042",
    : "2026-08-06",
    : [],
  },
  : [
    {
      : "factur-x.xml",
      : ,
      : "text/xml",
      : "Factur-X 1.0 MINIMUM invoice data",
      : "data",
    },
  ],
});

The renderer checks PDF conformance while writing. It does not check the invoice XML, tax rules, or whether a receiving system accepts the file.

Takumi does not build the invoice XML. Use a library for your profile, or write the elements the profile lists. The e-invoice example writes a MINIMUM profile by hand, along with the facturXmp schema used above.

Validating the result

Two validators cover the two halves. Both need Java.

veraPDF checks the container:

Validate PDF/A and PDF/UA
verapdf --flavour 3b --format text invoice.pdf
verapdf --flavour ua1 --format text invoice.pdf

Mustang checks the Factur-X half, both the XML and the packaging:

Validate Factur-X with Mustang
java -jar Mustang-CLI.jar --action validate --source invoice.pdf

Mustang finds the profile through the fx: XMP block. Takumi writes each value together with the schema description PDF/A requires, so the two cannot disagree. It does not check the values, so a wrong profile name shows up in Mustang, not at render time.

Accessible invoices

For PDF/UA-1 output, set tagged: "ua1", lang, and metadata.title. Then review the reading order and table structure yourself. See PDF/A and PDF/UA.

Reproducible bytes

Same content, resources, renderer version, and metadata.creationDate produce the same bytes. A new date changes the file even when the invoice looks the same.

Last updated on