HTML PDF
Page breaks that
don't cut your table in half.
A real browser engine on the server for selectable, vector text and true CSS pagination — and a self-contained renderer in the browser when Chromium isn't there. One API across Node, Bun, and the browser, with no required runtime dependencies.
npm i htmlpdfx
Start here
Getting started
Install it and render your first PDF in about a minute.
Choose an engine
There are two, they are not equal, and picking the right one is most of the job.
Page breaks
Forced breaks, and blocks that must never be split across a page.
Two engines, one API
toPdf() behaves the same everywhere. What changes is what's rendering underneath — and that difference is worth understanding before you ship, because it decides whether your text is selectable.
Chromium
The browser's own print-to-PDF, driven by Puppeteer or Playwright. The PDF is the webpage: page breaks, fonts, and layout are correct by construction, and text stays selectable and searchable. The default on Node, Bun, and Deno, and the one to reach for when a person will read the result.
Canvas
A from-scratch DOM rasterizer for the browser, with no Chromium and nothing to install. It produces a faithful screenshot of your DOM, sliced cleanly across pages — a raster, not a reflow. Use it as graceful degradation, not as a substitute.
auto picks per runtime, so you rarely choose by hand. See what each engine supports for the full matrix, row by row.
What it does differently
No split rows or cards
The slicer backs up to the top of a table row, card, or figure rather than cutting through it — and <thead> repeats on every page a table spans.
Selectable, not screenshotted
Real vector text in the Chromium tier, so readers can search and copy from the PDF, and the file stays small.
No jsPDF, no html2canvas
The PDF writer and the DOM rasterizer are written from scratch. The core has no required runtime dependencies at all.
Modern CSS as-is
Flexbox, grid, custom properties, oklch() and lab() colours, and web fonts — awaited before render, then embedded.
Honest about its limits
Every capability is marked per engine, and the gaps are published rather than glossed over. Knowing a feature is best-effort beats discovering it in a broken PDF.
Familiar on day one
A functional toPdf() plus an html2pdf.js-compatible fluent builder, so migrating is mostly deleting code.
Install
npm i htmlpdfxOn Node and Bun that also brings Puppeteer as an optional dependency, so the high-fidelity engine works without a second step. In the browser there's nothing else to install.