Takumi

Comparison

takumi-pdf against Puppeteer and react-pdf, measured.

This comparison benchmarks three JavaScript PDF renderers. The benchmark uses an 80-line invoice. The invoice spans two pages and has a page-number footer.

Each harness uses its engine's native template language. Geometry, columns, and the embedded font (Inter 400/700) are the same in each harness. react-pdf's pt values mirror the other renderers' px values. takumi and Chrome produce the same two-page layout.

Warm figures are the median of 20 renders. The bench source reproduces this table. Run it with bun bench.

Environment: Apple M1 Pro, macOS 15.7.4, Bun 1.3.14, Chrome 151.

takumi-pdf 0.4@react-pdf/renderer 4.5.1Puppeteer + Chrome
Cold start to first PDF176 ms495 ms0.7–2.8 s
Warm render (median)26 ms236 ms198 ms
Output size19 KB (15 KB with tagged: false)16 KB52 KB
Deploy needs1.5 MB gzip wasmpure JSChrome install (hundreds of MB)
Template languageJSX, HTML, node trees with CSS and Tailwindits own primitives (<View>, <Text>, StyleSheet)HTML with full CSS
Selectable text, subset fontsyesyesyes
Arabic and bidi textyesshaping only, manual directionyes
Runs on edge runtimesyes (Cloudflare Workers)no (Node)no

Bundle and install size

These measurements use npm-registry packages and esbuild (--bundle --minify, gzip). Install size comes from a clean bun add.

JS bundle (min+gzip)wasm (gzip)node_modules
takumi-pdf 0.410 KB1.52 MB (1.16 MB brotli)4.2 MB, 45 files
@react-pdf/renderer 4.5.1493 KB32 MB, 1998 files
pdf-lib 1.17.1179 KB26 MB
pdfkit 0.19.1253 KB23 MB
jspdf 4.2.1248 KB59 MB
Puppeteer + ChromeChrome install (hundreds of MB)

pdf-lib, pdfkit, and jspdf are drawing primitives. You position each text line yourself. They are not in the benchmark above.

takumi-pdf's wasm binary is the largest single JS download. It is also the whole engine. node_modules has 45 files.

Cloudflare Workers caps a free-plan bundle at 3 MB after compression. The 1.52 MB wasm and the 10 KB JS wrapper leave half of that for application code.

Reading the numbers honestly:

  • Chrome's cold start includes launching a browser process. It varies with machine state. Its memory spans several processes. A single-process measurement misses this. Chrome performs well after warming. It has the most complete CSS support of the three.
  • react-pdf embeds the same Inter subsets as the other two. Subsetting on every render is most of its warm cost; the standard 14 PDF fonts avoid it. Documents use react-pdf's component set. Existing HTML, JSX, and Tailwind markup cannot be reused. It shapes Arabic, but paragraph direction is manual, and our Arabic sample dropped its first letter.
  • takumi-pdf reuses OG-image components unchanged. Its CSS coverage is narrower than Chromium's. PDF output does not support filter: blur(), drop-shadow(), or backdrop-filter. A blurred box-shadow is approximated with bands, and a blurred text-shadow draws sharp. Tagged output is on by default and adds about 4 KB to this invoice, down from 10 KB before the structure tree moved into an object stream. tagged: false removes it.

Rule of thumb: use Puppeteer to reproduce a complex web page pixel-perfectly. Any renderer suits documents written from scratch for PDF work. Use takumi-pdf to reuse Takumi image components. It suits edge runtimes. It also offers lower per-document latency.

Last updated on

On this page