html4pdf vs running Chromium in a Lambda
Every serverless platform has a blog post about this, and every one of them is longer than it should be. That length is the comparison.
The short answer
It works, it is free, and it is the single most fiddly part of most codebases that do it. If your function is already near the size limit or your users wait on the first request, the trade has stopped paying.
What serverless Chromium is
The pattern is puppeteer-core, which is Puppeteer without a bundled browser, plus a Chromium build stripped down to fit a function package, usually @sparticuz/chromium. You launch it with a specific set of flags, render, and close. It is free, it runs inside your own infrastructure, and it is the standard answer on AWS Lambda, Vercel and Netlify.
Side by side
| serverless Chromium | html4pdf | |
|---|---|---|
| Package size | Close to the 50MB limit before your own code | Nothing added |
| Cold start | Several seconds to unpack and boot the browser | None |
| Memory | 1GB or more configured per function | Not your concern |
| Maintenance | The Chromium build must track the runtime and the library | None |
| Fonts | You load them yourself, the image has almost none | Chrome with a normal font stack |
| Timeouts | A render that hangs burns the whole function timeout | Bounded by the request |
| Cost | Function time, which includes browser boot | From $0.033 per document |
| Data | Never leaves your account | Rendered in memory, never stored |
| Edge runtime | Not possible | Works, it is a fetch |
Choose serverless Chromium when
- Documents must stay inside your own cloud account for policy reasons.
- You generate enough volume that function time is cheaper than per-document pricing.
- You have already done the work and it is stable.
- You need to control the exact Chrome version and flags.
Choose html4pdf when
- Your function is bumping the size limit and you are deleting dependencies to fit.
- A user is waiting on the request and the cold start is the slowest part of it.
- You are debugging missing fonts and emoji in a stripped Chromium image.
- You want this feature to stop being a thing anyone thinks about.
What the change looks like
The browser lifecycle, the flags and the stripped Chromium dependency all go. What is left is the request.
import chromium from "@sparticuz/chromium"
import puppeteer from "puppeteer-core"
const browser = await puppeteer.launch({
args: chromium.args,
executablePath: await chromium.executablePath(),
headless: true,
})
const page = await browser.newPage()
await page.setContent(html, { waitUntil: "networkidle0" })
const pdf = await page.pdf({ format: "A4", printBackground: true })
await browser.close()const res = await fetch("https://api.html4pdf.com/generate-pdf", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.HTML4PDF_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
html,
responseFormat: "binary",
options: { format: "A4", printBackground: true },
}),
})
const pdf = Buffer.from(await res.arrayBuffer())See the code
The working example, in the language this decision is being made in.
serverless Chromium: common questions
Can I run Puppeteer on Vercel?
Yes, with puppeteer-core and @sparticuz/chromium. Expect to spend time on the function size limit, the memory setting and cold starts, and expect to revisit it when the runtime version changes.
Why is my serverless PDF missing fonts?
The stripped Chromium image ships almost no fonts, so anything not embedded falls back to whatever is there, which is often nothing. You load fonts yourself, or embed them in the document.
How slow is the cold start?
Several seconds before your code runs, because the browser has to be unpacked and started. Warm invocations are much faster, which is exactly the shape users notice.
Is it cheaper than an API?
At volume, yes. At low or spiky volume the function time spent booting a browser, plus the memory you had to provision for it, closes the gap faster than people expect.
Try it before you decide
New accounts get 10 free credits, no card. Send the HTML you already have and compare the output against whatever you are using now.
Other comparisons
- html4pdf vs PuppeteerRun Puppeteer yourself if PDFs are high volume and you already operate containers. Use an API if PDFs are a feature of your product and you do not want Chrome in your deployment.
- html4pdf vs wkhtmltopdfwkhtmltopdf is still fine for simple documents you already have working. For anything new, or anything using modern CSS, its archived WebKit engine is the problem you will eventually hit.
- html4pdf vs WeasyPrintIf your document is HTML and CSS with no scripts, WeasyPrint is excellent and free, and you should use it. If anything on the page is drawn by JavaScript, you need a browser.
- html4pdf vs DompdfDompdf is the right answer for a simple document on shared hosting with no extra services. It is the wrong answer for anything designed with modern CSS, because it implements CSS 2.1.
- html4pdf vs jsPDF and html2canvasjsPDF with html2canvas is free and needs no backend, and it produces a picture of your page rather than a document. If the text has to be selectable, searchable or printable at quality, it is the wrong tool.
- html4pdf vs @react-pdf/rendererUse react-pdf when the document is small, stable, and worth building as its own component tree. Use a renderer when the PDF should be the same thing your users already see on screen.
- html4pdf vs printing from the browserIf the user is the one who wants the PDF and is sitting in front of the page, use window.print() and a print stylesheet. If your system needs the file, you need a renderer.
- html4pdf vs PlaywrightPlaywright is the better browser automation library. It is not a better PDF pipeline, because the cost was never the library: it is operating the browser. Use it if you already run it, call an API if you do not.
- html4pdf vs GotenbergGotenberg is the right answer if you want this capability inside your own infrastructure and have somewhere to run a container. We are the right answer if you want it without running anything.
- html4pdf vs mPDFmPDF is the best free answer in PHP for long, table-heavy documents written as documents. It is still its own engine, so a design built in a browser will not arrive intact.
- html4pdf vs ReportLabReportLab is the right tool when the document is a fixed, precise artefact and you want no dependencies. It is the wrong tool when the design changes often, because every change is a code change.
- html4pdf vs iTextIf you need deep PDF manipulation on the JVM and can accept AGPL or buy a licence, iText is the strongest tool available. If you only need HTML turned into a document, the licence question is one you can skip entirely.
- html4pdf vs QuestPDFQuestPDF is excellent for documents you are happy to describe in C#. If the document already exists as a web page, you would be rebuilding it, and that duplication is the whole cost.
- html4pdf vs PDFKitPDFKit is a good fit for simple, fixed documents generated at volume, where no browser and no network is worth writing the layout by hand. Beyond that the hand-written layout becomes the problem.