My exam platform, mcqplex.com, turns HTML templates (reports, certificates) into PDFs with Puppeteer. Like many tutorials, my code launched and closed a full Chromium for every PDF:
const browser = await puppeteer.launch();
// ...render...
await browser.close();
It worked, but it was slow, and memory jumped when many downloads came in at once.
Keeping one browser warm
const browser = await puppeteer.launch(); // once, at startup
async function renderPdf(html) {
const context = await browser.createBrowserContext(); // separate cookies/storage
const page = await context.newPage();
try {
await page.setContent(html, { waitUntil: 'domcontentloaded' });
return await page.pdf({ format: 'A4' });
} finally {
await context.close();
}
}
On my 2-core laptop, a render went from about 1.9 s to about 0.66 s. Your numbers will depend on your machine.
Images still delayed printing
My templates load a school logo from a remote URL. I used domcontentloaded so a slow image host wouldn't block the render, but page.pdf() still waited for the pending image. It waits for fonts to be ready by default, and that didn't finish while the request was hanging.
Calling window.stop() before printing stopped the wait, but it also removed logos that loaded only a little slowly. A short grace period worked better for me:
await page.waitForNetworkIdle({ idleTime: 100, timeout: 3000 }).catch(() => {});
await page.evaluate(() => window.stop());
Slow logos still show up, and a broken one is left blank after 3 seconds. I only found both issues by testing with real Chromium. My mocked tests didn't catch them.
The package
In production I also needed a concurrency limit, a queue limit, timeouts and crash recovery, so I put it all into a small package, pdf-render-pool:
const { createPdfRenderPool } = require('pdf-render-pool');
const { renderPdf } = createPdfRenderPool({ concurrency: 2, maxQueue: 50 });
GitHub: https://github.com/rcgiri-physics/pdf-render-pool
If you try it, I'd be glad to hear how it works for you, especially on Linux servers.
Top comments (0)