html4pdf vs Puppeteer

This is not really a comparison of two renderers. Both are headless Chrome, so the output is the same. What differs is who runs the browser.

The short answer

Run 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.

What Puppeteer is

Puppeteer is a Node library that drives Chrome or Chromium over the DevTools protocol. You launch a browser, open a page, set its content, and call page.pdf(). It is free, it is maintained by the Chrome team, and because the renderer is Chrome, whatever you see in your browser is what you get in the document.

Side by side

 Puppeteerhtml4pdf
Rendering engineHeadless ChromeHeadless Chrome
CSS and JavaScriptEverything Chrome supportsEverything Chrome supports
What you deployYour app plus a Chromium binary, a few hundred megabytesNothing. One HTTPS call
ServerlessPossible with a stripped Chromium build, close to size limits, slow cold startsWorks anywhere a fetch works
MemoryA browser process per render, which you size and cap yourselfNot your problem
Cost per documentWhatever your own compute costsFrom $0.033, one credit
Ongoing workChrome updates, zombie processes, concurrency limitsNone
DataStays inside your infrastructureRendered in memory, never stored
LanguagesNode, or a port such as Pyppeteer or PuppeteerSharpAny language with HTTP

Choose Puppeteer when

  • You render thousands of documents a day and the per-document cost matters more than the operational cost.
  • You already run containers and have somewhere sensible to put a browser.
  • You need more than a PDF: screenshots, scraping, interacting with the page before printing.
  • Your documents must never leave your own infrastructure, even in memory.

Choose html4pdf when

  • PDFs are a feature, not your product, and you do not want to operate a browser for them.
  • You deploy to Lambda, Vercel, Cloud Run or anywhere a 300MB dependency hurts.
  • You are not writing Node, and the ports are a worse fit than an HTTP call.
  • You want the first version shipped this afternoon.

What the change looks like

The shape of the change: everything from launch to close becomes one request, and the PDF buffer arrives the same way.

before.js
import puppeteer from "puppeteer"

const browser = await puppeteer.launch({ args: ["--no-sandbox"] })
const page = await browser.newPage()

await page.setContent(html, { waitUntil: "networkidle0" })
const pdf = await page.pdf({ format: "A4", printBackground: true })

await browser.close()
after.js
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.

Puppeteer: common questions

Is html4pdf just Puppeteer as a service?

Effectively, yes, and we would rather say so than pretend otherwise. The renderer is headless Chrome. What you are buying is not a better engine, it is not having to run one.

Will my PDFs look different if I switch?

They should not. Same engine, same CSS support, same page options. Fonts and stylesheets need absolute URLs either way.

Why is Puppeteer hard in serverless?

A full Chromium does not fit inside most function size limits, so you need a stripped build such as @sparticuz/chromium, and the first request after a cold start pays several seconds to boot the browser.

Which is cheaper?

At high volume, your own Puppeteer, by a lot. At low and spiky volume, an API, once you count the instance that has to stay warm and the hours spent on memory leaks.

Can I use both?

Plenty of teams do: the API while the feature is small, their own renderer once volume justifies it. Because the body is plain JSON, swapping back is a contained change.

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.

Get API key

Other comparisons