Blog
How to create PDFs with Puppeteer
Puppeteer makes excellent PDFs because it is a real browser. Here is a clean example, the options that actually matter, and the honest list of what breaks when you run it in production.
A minimal, correct example
The two things people miss: wait for the network to settle so fonts and images load, and always close the browser in a finally block so a thrown error does not orphan a process.
import puppeteer from "puppeteer";
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.setContent(html, { waitUntil: "networkidle0" });
const pdf = await page.pdf({
format: "A4",
printBackground: true,
margin: { top: "1cm", bottom: "1cm", left: "1cm", right: "1cm" },
});
} finally {
await browser.close(); // always close, even on error
}The print options that matter
- printBackground: true, or your backgrounds and colors vanish.
- waitUntil: networkidle0, so web fonts and images finish loading.
- format and margin, to match the paper size you actually print on.
- break-inside: avoid in your CSS, so tables do not split mid-row.
Where it gets hard
Memory
Long-running workers leak until they are OOM-killed. You end up recycling browsers after N renders.
Concurrency
Each browser is heavy. Without a strict limiter, parallel renders time out or crash the tab.
Serverless
A full Chromium binary does not fit most functions, so you fight chromium layers and cold starts.
When to stop running it yourself
If you are recycling browsers, tuning concurrency, and chasing OOM kills, you are operating infrastructure instead of shipping product. At that point a render API gives the same Puppeteer-quality output with none of the operations.
Frequently asked
Why do my Puppeteer PDFs miss backgrounds?
Set printBackground: true in page.pdf(). By default Chrome omits background colors and images in print.
Why are fonts wrong in my Puppeteer PDF?
Wait for fonts to load before rendering, for example with waitUntil: networkidle0 and document.fonts.ready, so the page is fully styled when you call page.pdf().
500 free documents a month, flat pricing after that, and a live playground you can try without signing up.