How to generate PDFs in Node.js (5 methods compared)
Draft article. Add benchmark charts, screenshots, and SDK examples during the review pass.
Generating PDFs in Node.js sounds straightforward until real product constraints show up. The first demo usually works. The production version is where things get messy: invoices need exact spacing, tables grow unexpectedly, branding has to stay consistent, and support tickets appear when one browser update changes rendering.
This draft compares five common approaches so the article can later become a practical buying and implementation guide.
Who this guide is for
- SaaS teams generating invoices, contracts, reports, shipping labels, or receipts.
- Founders deciding whether to build a rendering stack or buy one.
- Engineers replacing a fragile PDF implementation that no longer scales.
The 5 methods at a glance
| Method | Best for | Main advantage | Main downside |
|---|---|---|---|
| PDFKit-style libraries | Fully custom programmatic layouts | Fine-grained control | High implementation effort |
| Headless browsers | HTML/CSS-based documents | Strong layout fidelity | Operational overhead |
| HTML-to-PDF libraries | Simpler HTML rendering use cases | Fast to prototype | Mixed fidelity on complex layouts |
| Hosted PDF APIs | Product teams shipping quickly | Minimal infrastructure | External vendor dependency |
| Template-based document APIs | Repeatable business documents | Strong reuse and maintainability | Requires template workflow discipline |
1. Programmatic PDF libraries
Libraries such as PDFKit generate documents by drawing text, lines, images, and shapes directly in code. This approach works well when the document structure is highly custom and you do not want to rely on HTML.
When it fits
- Labels, tickets, receipts, or tightly controlled internal forms.
- Documents with a stable structure and minimal styling complexity.
- Cases where engineering wants explicit control over every element.
Tradeoffs
- Layout logic becomes application logic.
- Multi-page flows, tables, and wrapping require custom work.
- Non-engineers cannot easily edit document design.
import PDFDocument from "pdfkit";
const doc = new PDFDocument();
doc.fontSize(24).text("Invoice", 72, 72);
doc.fontSize(12).text("Customer: Acme Corp", 72, 120);
doc.text("Total: $1,500.00", 72, 160);
doc.end();
Image placeholder: show a simple PDFKit-generated invoice with visible coordinate-based layout.
2. Headless browser rendering
This is the most common path when your team already thinks in HTML and CSS. You render a page using Playwright or Puppeteer, then print it to PDF.
Why teams like it
- Reuses frontend skills and existing design systems.
- Handles typography, spacing, and print styles better than many legacy converters.
- Easier to maintain than hand-positioning every element in code.
Where it hurts
- Browser processes are heavier than plain Node.js libraries.
- Throughput, sandboxing, and cold starts matter in serverless setups.
- Debugging print CSS can still be time-consuming.
const browser = await chromium.launch();
const page = await browser.newPage();
await page.setContent(renderedHtml);
await page.pdf({ format: "A4", printBackground: true });
await browser.close();
3. Traditional HTML-to-PDF libraries
These tools convert HTML into PDF without running a full browser engine. They can be useful for simpler documents, especially when speed of setup matters more than perfect fidelity.
Best use cases
- Basic reports, confirmations, receipts, or simple exports.
- Internal tools where pixel-perfect output is not critical.
Risks to call out in the final article
- CSS support can be incomplete.
- Complex layouts may break differently across tools.
- Tables, page breaks, and embedded assets often need extra testing.
4. Hosted PDF generation APIs
This option moves the rendering infrastructure outside your application. Your app sends structured data or HTML to an API and receives the final PDF back.
What makes this attractive
- Faster time to market.
- No browser fleet or rendering workers to maintain.
- Easier to scale document generation independently from the main app.
What to evaluate
- Latency and regional availability.
- Template versioning.
- Error reporting and preview workflows.
- Authentication, rate limits, and cost model.
5. Template-based document APIs
This is often the sweet spot for recurring business documents. Instead of writing document layout in code every time, you define a reusable template and inject dynamic data into it through an API.
Why this model tends to age well
- Templates make document changes less risky.
- Payloads stay clean and predictable.
- Product, operations, and engineering can collaborate more easily.
{
"template_id": "invoice-v2",
"data": {
"invoice_number": "INV-2026-0042",
"customer": "Acme Corp",
"total": 1500
}
}
Comparison criteria to expand later
The finished article should compare each method across the dimensions below:
- Developer ergonomics
- HTML and CSS fidelity
- Throughput under load
- Template reuse
- Team maintainability
- Cost at scale
- Suitability for customer-facing documents
Recommended decision framework
If the team only needs one-off exports, a simple library may be enough. If the team needs reliable, branded, repeatable documents across many flows, a template-based API usually becomes more attractive over time.
That is the argument worth sharpening in the polished version of this article.
What to add in the final version
- A benchmark chart comparing latency by method.
- A visual table of maintenance cost versus output quality.
- SDK snippets for Node.js, Python, and PHP.
- A short DocGL example showing a template-first implementation.