From Puppeteer to screenshot APIs — every approach to generating PDFs in Node.js, with honest trade-offs for each.
PDF generation sounds simple — take some data, render it as a document. In practice, it involves font rendering, page layout, image embedding, and precise pixel positioning that web technologies handle very differently from PDF specifications. Every approach to PDF generation in Node.js makes a trade-off between rendering accuracy, library complexity, and infrastructure requirements.
For developers building invoices, reports, and data exports in Node.js applications in 2026, there are five main approaches. This guide covers each honestly, including the real-world edge cases that documentation rarely mentions.
PDFKit is a JavaScript library that creates PDFs programmatically. You define each element — text, images, lines, shapes — through a fluent API. The output is precise and lightweight. PDFKit works in any Node.js environment with no external dependencies.
const PDFDocument = require("pdfkit");
const fs = require("fs");
const doc = new PDFDocument({ margin: 50 });
doc.pipe(fs.createWriteStream("invoice.pdf"));
doc.fontSize(24).text("Invoice #1042", { align: "left" });
doc.fontSize(12).text(`Date: ${new Date().toLocaleDateString()}`);
doc.moveDown();
doc.text("Amount due: $299.00", { bold: true });
doc.end();
Best for: Simple, text-heavy documents where you control every element precisely. Financial reports with tables, certificates, receipts.
Not ideal for: Rendering HTML/CSS layouts. If your design lives in HTML and CSS, programmatically re-creating it in PDFKit is extremely time-consuming and the output rarely matches perfectly.
jsPDF runs in both browser and Node.js environments and can convert HTML to PDF using html2canvas. The HTML rendering quality is limited — complex CSS, web fonts, and modern layout features often render incorrectly. It works well for simple layouts but breaks on anything sophisticated.
Best for: Client-side PDF generation in browsers where server-side rendering is not an option. Simple forms and basic formatted text.
Not ideal for: Complex layouts, server-side rendering at scale, or documents that need to match what users see in their browser.
Launch a headless Chromium browser, navigate to your HTML page or template, and call page.pdf(). The output matches browser rendering exactly — fonts, shadows, flexbox, grid layouts, all render correctly. This is the gold standard for rendering accuracy.
const puppeteer = require("puppeteer");
async function generatePDF(htmlContent) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(htmlContent, { waitUntil: "networkidle0" });
const pdf = await page.pdf({ format: "A4", printBackground: true });
await browser.close();
return pdf;
}
Best for: Pixel-perfect rendering when you already have HTML/CSS templates. Matches browser output exactly.
Not ideal for: Serverless environments (Lambda, Vercel, Cloudflare Workers), Docker containers with size constraints, or teams that don't want to maintain browser infrastructure.
SnapAPI runs Chromium externally and exposes PDF generation as a REST API. You send a URL; you get back a CDN-hosted PDF. Same rendering quality as Puppeteer, zero infrastructure overhead.
const axios = require("axios");
async function generatePDF(url) {
const { data } = await axios.get("https://snapapi.pics/v1/screenshot", {
params: {
access_key: process.env.SNAPAPI_KEY,
url,
format: "pdf",
full_page: true,
},
});
return data.url; // CDN-hosted PDF URL
}
Best for: Any Node.js environment — serverless, containers, VPS. No browser infrastructure to manage. Works on Vercel, Lambda, Cloud Run, Fly.io.
Not ideal for: Use cases where you need the raw PDF buffer immediately in memory without a CDN round-trip (though you can fetch the CDN URL). Extremely high-volume generation where per-call API pricing exceeds self-hosting costs.
WeasyPrint is a Python library that converts HTML/CSS to PDF. Call it from Node.js via child_process.exec(). Renders CSS better than jsPDF but not as accurately as Chromium. Requires Python and WeasyPrint installed on the server.
Best for: Teams comfortable with Python tooling who need better CSS support than jsPDF without full browser overhead.
Not ideal for: Serverless environments, teams that prefer pure JavaScript, or complex CSS that requires Chromium-level rendering.
The decision tree is simple. If your PDF template lives in HTML/CSS and rendering accuracy matters: use SnapAPI (serverless/containers) or Puppeteer (if you control your own server). If you're building a simple document programmatically from structured data: use PDFKit. If you need client-side PDF generation: use jsPDF. Everything else is an edge case.
For the majority of SaaS products generating invoices, reports, and data exports, SnapAPI is the right choice: browser-accurate rendering, zero infrastructure, works everywhere. Start free with 200 PDFs/month.
The PDF generation decision comes down to three questions. First: does your template already exist as HTML and CSS? If yes, use a browser-based approach (SnapAPI or Puppeteer) rather than re-implementing it programmatically in PDFKit. Re-creating a complex CSS layout in PDFKit takes days and never quite matches the original.
Second: are you on a serverless or constrained environment? If yes, use SnapAPI. Puppeteer does not run on Lambda, Vercel, or Cloudflare Workers without significant hacks that sacrifice reliability. SnapAPI works anywhere fetch works — which is everywhere.
Third: what is your volume? Below 50,000 PDFs per month, SnapAPI's pricing is almost certainly cheaper than self-hosting when you factor in developer time. Above that volume, run the numbers for your specific infrastructure costs — but also factor in ongoing maintenance time, which compounds over months and years.
PDFKit: best for programmatic document creation from structured data, no HTML/CSS rendering, pure Node.js with no external dependencies. jsPDF: best for client-side PDF generation, limited CSS support, works in browsers. Puppeteer/Playwright: best for pixel-perfect HTML-to-PDF on servers you control, requires Chromium, does not work serverless. SnapAPI: best for pixel-perfect HTML-to-PDF anywhere — serverless, containers, VPS — with zero infrastructure, REST API, CDN-hosted output. WeasyPrint: best for teams comfortable with Python who need better CSS than jsPDF without full Chromium, requires Python runtime. For most Node.js SaaS products generating user-facing PDFs in 2026, the choice is between Puppeteer (if you self-host) and SnapAPI (if you prefer managed infrastructure). Both produce identical rendering quality. The difference is infrastructure overhead, operational complexity, and cost. SnapAPI eliminates all three. Start generating PDFs free — 200 per month, no credit card.
Putting these concepts together into a production-ready PDF export feature requires a few components beyond the API call itself. You need an HTML template for the content being exported, a server-side route that renders the template with the correct data, a time-limited signed URL mechanism if the content is private, the SnapAPI call to capture the rendered template as a PDF, and a caching layer to avoid re-generating identical PDFs. For SaaS invoices, this entire stack can be built in a day. For complex reports with dynamic charts, allow two to three days to get the template rendering correctly across different data sets.
The most common mistake teams make is trying to generate PDFs client-side using jsPDF when their template is a complex React, Vue, or Svelte component. Client-side PDF generation has severe CSS limitations and almost never matches what users see in their browser. Move PDF generation server-side using SnapAPI and the output will always be correct because it uses the same browser engine as your users. The implementation is actually simpler than client-side approaches once you have a signed URL pattern for authenticating the render route. Teams that make this switch report never looking back — the output quality improvement alone justifies the migration, and the elimination of CSS workaround maintenance saves ongoing engineering time every month thereafter. Start with the SnapAPI free tier and have production-quality PDF generation running by end of day.
The Node.js PDF generation landscape in 2026 is mature and well-understood. Choose your tool based on your rendering requirements and deployment constraints, not based on familiarity or inertia. If you are generating PDFs from HTML templates and deploying to any modern cloud environment, SnapAPI is almost certainly the right choice. One API key, one endpoint, zero infrastructure. Start your free account and generate your first PDF today.