How to Generate PDFs in Node.js (2026 Guide)
Compare every approach to PDF generation in Node.js: Puppeteer DIY, wkhtmltopdf, PDFKit, and API services. See real code for each option.
The PDF generation landscape in Node.js
Generating PDFs programmatically is one of those tasks that sounds simple but quickly becomes a rabbit hole. Here's an honest comparison of every major approach available in Node.js today.
---
Option 1: Puppeteer (DIY headless Chrome)
The most powerful option — and the most complex to maintain.
import puppeteer from 'puppeteer'
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox'],
})
const page = await browser.newPage()
await page.setContent('<h1>Invoice</h1><p>Total: $249</p>')
const pdf = await page.pdf({ format: 'A4' })
await browser.close()
Pros: Full control, pixel-perfect output, supports any CSS.
Cons: Chromium binary (~300MB), memory leaks in long-running processes, cold start latency, complex deployment (especially serverless), needs a browser pool for production, OS-level dependencies.
---
Option 2: wkhtmltopdf
A native binary that uses WebKit to render HTML to PDF.
import { exec } from 'child_process'
exec('wkhtmltopdf input.html output.pdf', (err) => {
if (err) throw err
console.log('Done')
})
Pros: Fast, lightweight, no Node.js dependencies.
Cons: Outdated WebKit engine (poor CSS Grid/Flexbox support), binary installation required, harder to use in Docker/serverless, no JavaScript execution.
---
Option 3: PDFKit (programmatic PDF generation)
Build PDFs from scratch using a drawing API — no HTML rendering.
import PDFDocument from 'pdfkit'
import fs from 'fs'
const doc = new PDFDocument()
doc.pipe(fs.createWriteStream('output.pdf'))
doc.fontSize(24).text('Invoice', { align: 'center' })
doc.fontSize(12).text('Total: $249')
doc.end()
Pros: Pure Node.js, no system dependencies, good for simple documents.
Cons: Can't use HTML or CSS — you draw everything manually. Complex layouts require significant code. Can't reuse existing HTML templates.
---
Option 4: HTMLPDF.dev API (recommended)
Skip the infrastructure entirely. One HTTP request, one PDF.
const response = await fetch('https://api.htmlpdf.dev/api/pdf', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
html: '<h1>Invoice</h1><p>Total: $249</p>',
format: 'A4',
margin: { top: '1in', bottom: '1in', left: '1in', right: '1in' },
}),
})
const pdf = await response.arrayBuffer()
fs.writeFileSync('invoice.pdf', Buffer.from(pdf))
Pros: No installation, no Chromium binary, no memory management, works identically in serverless/edge/Docker, latest Chrome rendering engine, full CSS support.
Cons: Requires internet access, costs money above the free tier.
---
Which should you choose?
| Scenario | Recommendation |
|---|---|
| Prototype or low volume | HTMLPDF.dev free tier |
| High volume, self-hosted | DIY Puppeteer with a browser pool |
| No HTML, simple documents | PDFKit |
| Legacy systems | wkhtmltopdf |
| Serverless / Vercel / Lambda | HTMLPDF.dev API |
For most teams, the API approach wins on total cost of ownership — you avoid maintaining Chromium infrastructure and the operational complexity that comes with it.
Try HTMLPDF.dev free →Ready to get started?
Create a free account and generate your first PDF in under 2 minutes.
Get started free