HTML to PDF in 2026: Puppeteer, wkhtmltopdf, WeasyPrint and Prince Compared
This guide has a free tool → Open browser-based HTML to PDF tool
Every team eventually needs to turn HTML into a PDF, and every team discovers the same thing: the browser you already trust to render your site does a mediocre job of rendering it onto paper.
This compares the four engines people actually reach for, on the dimension that decides the outcome, which is CSS Paged Media support. No benchmark numbers here, because latency depends entirely on your document and your hardware and a number from my laptop tells you nothing about yours.
The thing that actually breaks
Screens scroll. Paper does not. Everything hard about HTML to PDF comes from that one difference.
The CSS features that handle it live in the Paged Media and Fragmentation specs:
@pagefor page size, margins, and named page types@page :first,:left,:rightfor different margins on different pages- Margin boxes such as
@top-centerand@bottom-rightfor running headers and footers break-inside: avoidto stop a table row or card splitting across a pageorphansandwidowsfor stray lines at a page boundarycounter(page)andcounter(pages)for "Page 3 of 12"position: running()andelement()for headers that repeat with content
Support for these is where the engines genuinely differ. Everything else is roughly equivalent.
The four engines
Puppeteer / Playwright (headless Chromium)
Rendering: identical to Chrome, which is the strongest possible guarantee for modern CSS. Flexbox, grid, web fonts, and JavaScript-driven layout all just work.
Paged Media: partial. @page size and margins work. Margin boxes such as @top-center are not supported, so running headers and footers are done through Puppeteer's own headerTemplate and footerTemplate options, which are a separate mini-templating system with their own quirks and their own CSS scope. break-inside: avoid works reasonably. position: running() does not exist.
Cost: you are running a browser. Each render is a Chromium process with the memory profile that implies. This is the real operational cost and it is why so many teams end up moving it off their main server.
Best for: documents that look like web pages. Dashboards, receipts, one-page invoices, anything chart-heavy.
wkhtmltopdf
Rendering: built on a very old WebKit. No flexbox, no grid, no modern CSS. In practice you write a separate stylesheet from 2012 for it.
Status: the project is archived and no longer maintained. It still works, it is still in a lot of production systems, and it will not get security fixes.
Best for: existing systems that already depend on it. Nothing new.
Where it still wins: it is genuinely lightweight compared to running Chromium, and for simple table-based documents on constrained hardware that is not nothing. If your document is a plain table and you cannot afford a browser, this is still a defensible choice.
WeasyPrint
Rendering: a Python engine written specifically for print, not a browser. No JavaScript at all, which means your HTML must be final before it arrives.
Paged Media: strong. Margin boxes, page counters, named pages, and break-inside are properly supported. This is the point of the project.
Best for: server-generated documents where you control the HTML and there is no client-side rendering. Invoices, statements, reports, letters. If your document is generated from a template and never needed JavaScript, WeasyPrint is often the correct answer and it is free.
Limits: flexbox and grid support has improved but still trails a browser. Complex web layouts need reworking.
PrinceXML
Rendering: a dedicated commercial print engine.
Paged Media: the most complete implementation of the four. Running elements, footnotes, cross-references, PDF/A output, and the fiddly typographic controls that matter for books and legal documents.
Cost: commercial licence, and it is not cheap.
Where it beats everything else: if your output is a book, a contract, or anything where a typographer would notice the difference, Prince produces better pages than headless Chromium and it is not close. That is worth saying plainly even though it costs money and the others do not.
Comparison
| Headless Chromium | wkhtmltopdf | WeasyPrint | PrinceXML | |
|---|---|---|---|---|
| Modern CSS (grid, flex) | Full | No | Partial | Good |
| JavaScript | Yes | Limited | No | No |
@page size and margins | Yes | Partial | Yes | Yes |
| Margin boxes (headers/footers) | No, uses its own templates | No | Yes | Yes |
| Page counters | Via templates | Partial | Yes | Yes |
break-inside: avoid | Mostly | Poor | Yes | Yes |
| Running elements | No | No | Partial | Yes |
| Maintained | Yes | No, archived | Yes | Yes |
| Licence | Free | Free | Free | Commercial |
| Resource cost per render | High | Low | Low | Low |
Choosing
Answer two questions.
Does the document need JavaScript to reach its final state? If yes, you need a browser engine and the shortlist is Chromium. If no, WeasyPrint and Prince open up, and both handle paged output better.
Does it look like a web page or like a document? Charts, cards, and brand layout point to Chromium. Page numbers, running headers, footnotes, and precise pagination point to WeasyPrint or Prince.
Most teams pick Chromium because it matches what they already know, then spend a week fighting headerTemplate to get "Page 3 of 12" into the footer. That week is the tell that the document was never a web page.
The part nobody budgets for
Choosing the engine is the easy half. Running it is the other half:
- Chromium in production wants process isolation, memory limits, and a restart policy, because a single malformed document can hang a worker.
- Fonts must be installed in the rendering environment. This is the single most common cause of "it works locally and looks wrong in production".
- Rendering a user-supplied URL is a server-side request forgery risk. If you let users pass a URL, you need to resolve and validate the host, block private address ranges, and re-check on every redirect.
- Concurrency limits are not optional. PDF rendering is the classic way to take down a web server with ten simultaneous requests.
This is why HTML-to-PDF-as-a-service exists as a category. Not because the rendering is hard, but because the operations around it are tedious and easy to get subtly wrong.
If you would rather not run any of it, PDFPipe is our own API for this, free for 100 documents a month. Its write-up of the Puppeteer tradeoff covers the same engine question from the hosted side, including where running Chromium yourself is still the better call. And if you only need to convert something once, the browser-based HTML to PDF tool here does it locally without uploading anything.
Further reading
- Browser tools, headless browsers, and PDF APIs compared, on when each approach fits
- Client-side vs server-side tools
Which engine did you end up on, and what was the specific CSS feature that forced the decision?
Related Tools
Free, private, no signup required
PDF Merge & Split
Merge PDF online free or split PDF into pages - no upload to servers, all processing happens in your browser
JSON Formatter
JSON formatter and validator online - format, beautify, and validate JSON data instantly in your browser
Regex Tester
Free online regex tester - test and debug regular expressions with live matching and highlights
Text Diff Checker
Free online text diff checker - compare two texts and see the differences highlighted line by line
You might also like
8 min read
How to Generate PDFs from HTML - Browser Tools, Headless Browsers, and PDF APIs
9 min read
How to Add an AI Chat Widget to Your Website - Live Chat, Platforms, and Rolling Your Own
9 min read
Best Free Markdown Editors Compared - Dillinger and StackEdit Alternatives
Want the REST API, the MCP server, and an ad-free read?