How to Generate a PDF from HTML in Node.js (Without Puppeteer)
The canonical Node.js answer for HTML-to-PDF is Puppeteer — but it pulls 200–400MB of Chromium into your dependency tree and breaks in serverless. Here's the one-fetch alternative.
The canonical Node.js answer for HTML-to-PDF is Puppeteer: spin up a headless Chromium, navigate to a page or set content, call page.pdf(). It works, but it pulls Chromium into your dependency tree, adds 200–400MB to your deployment, and breaks in serverless environments unless you configure a Chromium layer.
Here's the one-fetch alternative:
Basic usage
import fs from 'fs';
const html = `<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: system-ui, sans-serif; padding: 40px; }
h1 { font-size: 24px; margin-bottom: 8px; }
.amount { font-size: 32px; font-weight: bold; color: #111; }
</style>
</head>
<body>
<h1>Invoice #1042</h1>
<p>Due: March 1, 2026</p>
<div class="amount">$429.00</div>
</body>
</html>`;
const res = await fetch('https://pagebolt.dev/api/v1/pdf', {
method: 'POST',
headers: {
'x-api-key': process.env.PAGEBOLT_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({ html })
});
fs.writeFileSync('invoice.pdf', Buffer.from(await res.arrayBuffer()));
No browser. No Chromium. One fetch, one file.
Capture a live URL instead
If the content is already on a URL (a hosted invoice, a report page, a dashboard), pass url instead of html:
const res = await fetch('https://pagebolt.dev/api/v1/pdf', {
method: 'POST',
headers: { 'x-api-key': process.env.PAGEBOLT_API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({
url: 'https://yourapp.com/invoices/1042',
blockBanners: true
})
});
Use in an Express route
import express from 'express';
const app = express();
app.get('/invoices/:id/pdf', async (req, res) => {
const invoice = await getInvoice(req.params.id); // your data fetch
const html = renderInvoiceHtml(invoice); // your template function
const capture = await fetch('https://pagebolt.dev/api/v1/pdf', {
method: 'POST',
headers: { 'x-api-key': process.env.PAGEBOLT_API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ html })
});
const pdf = Buffer.from(await capture.arrayBuffer());
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', `attachment; filename="invoice-${req.params.id}.pdf"`);
res.send(pdf);
});
Use in AWS Lambda / Vercel Functions
No extra config needed. The capture is an outbound HTTPS call — it runs in any serverless environment without Chromium layers, memory tuning, or cold-start mitigation.
// Lambda handler
export const handler = async (event) => {
const { html } = JSON.parse(event.body);
const res = await fetch('https://pagebolt.dev/api/v1/pdf', {
method: 'POST',
headers: { 'x-api-key': process.env.PAGEBOLT_API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ html })
});
const pdf = Buffer.from(await res.arrayBuffer());
return {
statusCode: 200,
headers: { 'Content-Type': 'application/pdf' },
body: pdf.toString('base64'),
isBase64Encoded: true
};
};
CSS in generated PDFs
All CSS in your <style> block renders in the PDF. A few practical notes:
- Use
ptorpxunits —em/remrelative to viewport can behave unexpectedly in print context - For page breaks, use
page-break-before: alwaysorbreak-before: page - Web fonts work if you include a
<link>tag pointing to a publicly accessible font URL - Print-specific styles can be added in a
@media print {}block
Beyond PDFs: add a narrated walkthrough
If you want to show users how a document was generated — useful for invoice portals or report builders — record a narrated video of the flow in the same API call pattern:
const res = await fetch('https://pagebolt.dev/api/v1/video', {
method: 'POST',
headers: { 'x-api-key': process.env.PAGEBOLT_API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({
steps: [
{ action: 'navigate', url: 'https://yourapp.com/invoices/1042', note: 'Open the invoice' },
{ action: 'click', selector: '#download-pdf', note: 'Download as PDF' }
],
audioGuide: {
enabled: true,
voice: 'nova',
script: "Here's your invoice. {{1}} {{2}} One click to download."
},
pace: 'slow'
})
});
fs.writeFileSync('invoice-demo.mp4', Buffer.from(await res.arrayBuffer()));
Same pattern, one API, no extra tools.
Get Started Free
100 requests/month, no credit card
Generate PDFs from HTML or live URLs — no Puppeteer, no Chromium, no browser to manage.
Get Your Free API Key →