Recommended Free Tools
For Node.js PDF generation, choose PDFKit to draw a document from code, pdf-lib to create or edit PDF structures, and Puppeteer to print an existing HTML/CSS page. There is no evidence-based universal winner: the right tool depends on the format your content is already in and whether you need to modify an existing PDF.
Choose the PDF approach that matches your source
| Tool | Best fit | Main tradeoff |
|---|---|---|
| PDFKit | Building printable documents from drawing primitives such as text, shapes, images, tables, and forms | Node output is stream-based. Browser support has a narrower stream interface and cannot directly read filesystem paths. |
| pdf-lib | Creating and modifying documents, including page operations, forms, and embedding pages | You work with an explicit PDF-document editing model. Custom fonts require the documented fontkit integration. |
| Puppeteer | Printing a page already laid out in HTML and CSS | It automates browser printing rather than providing a direct PDF drawing API. |
Use these questions to decide:
- Is the document already HTML? Use Puppeteer when its browser-rendered layout and print styles should determine the PDF.
- Are you composing pages from code? PDFKit provides drawing, layout, image, table, form, and related PDF-generation features.
- Must you load or change an existing PDF? pdf-lib documents loading and modifying files as well as creating new ones.
- Must the same code work beyond Node.js? pdf-lib documents support for Node, browsers, Deno, and React Native. PDFKit also supports Node and browsers, with different runtime constraints.
The official documentation describes capabilities, not a same-workload speed or memory comparison. If throughput or resource use decides the choice, benchmark your own document sizes, assets, and deployment environment rather than treating these tools as a performance ranking.
Generate a PDF with PDFKit
PDFKit is a drawing-oriented JavaScript library for Node.js and browsers. Its documented capabilities include text layout, vector paths and transformations, TrueType/OpenType/WOFF font embedding, JPEG and PNG images, tables, annotations, AcroForms, outlines, and security options. It suits multi-page printable documents assembled from application data.
Install and write a file in Node.js
Install the package with npm install pdfkit. This ES module example creates a document, pipes it to a writable file stream, writes content, and calls end() to finalize the PDF:
#1 Best Overall
import { createWriteStream } from 'node:fs';
import { PDFDocument } from 'pdfkit';
const doc = new PDFDocument({ size: 'A4', margin: 50 });
doc.pipe(createWriteStream('report.pdf'));
doc.fontSize(20).text('Monthly report', { align: 'center' });
doc.moveDown();
doc.fontSize(12).text('Generated from Node.js data.');
doc.end();
The package documentation also shows CommonJS use with the named export: const { PDFDocument } = require('pdfkit'). Use the import style supported by your project configuration.
Stream to an HTTP response
In Node, a PDFKit document is a readable stream. It can be piped to an HTTP response instead of a file. Set the response headers before piping so the client handles the output as a PDF:
app.get('/report.pdf', (req, res) => {
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', 'inline; filename="report.pdf"');
const doc = new PDFDocument({ size: 'A4', margin: 50 });
doc.pipe(res);
doc.fontSize(18).text('Report');
doc.end();
});
In a real application, make sure every success and error path is handled by the surrounding server framework. Do not omit doc.end(): it signals that document writing is complete.
Assets and browser use
The Node build has filesystem access, native zlib compression, and Node streams. The browser build omits Node’s stream module and filesystem access. If a browser capture uses an image or font from a filesystem path, register or provide its bytes rather than expecting the browser build to read that path directly. PDFKit’s toBlob and toBytes helpers under pdfkit/output are described as experimental, so do not make them a production dependency without checking their current status in the project documentation.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsRank #2
Create or edit a PDF with pdf-lib
pdf-lib uses an explicit document model: create or load a document, perform page and content operations, then serialize it. Its documentation covers adding, inserting, and removing pages; drawing text, images, and vector graphics; embedding pages; and creating or filling forms. The project says it is written in TypeScript, compiled to pure JavaScript, and works in Node.js, browsers, Deno, and React Native.
Create a new PDF
Install with npm install --save pdf-lib. The following creates a one-page PDF and writes the serialized bytes:
import { writeFile } from 'node:fs/promises';
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';
const pdfDoc = await PDFDocument.create();
const page = pdfDoc.addPage([595.28, 841.89]);
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
page.drawText('Monthly report', {
x: 50,
y: 780,
size: 20,
font,
color: rgb(0.1, 0.2, 0.4),
});
const bytes = await pdfDoc.save();
await writeFile('report.pdf', bytes);
Load and modify an existing PDF
Use PDFDocument.load() when you need to edit a PDF rather than print an HTML page. This example adds text to the first page, then saves a new file:
import { readFile, writeFile } from 'node:fs/promises';
import { PDFDocument, StandardFonts } from 'pdf-lib';
const input = await readFile('source.pdf');
const pdfDoc = await PDFDocument.load(input);
const pages = pdfDoc.getPages();
if (pages.length === 0) {
throw new Error('The source PDF has no pages');
}
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
pages[0].drawText('Reviewed', { x: 50, y: 40, size: 10, font });
await writeFile('reviewed.pdf', await pdfDoc.save());
For a custom font, the documented approach registers @pdf-lib/fontkit before embedding the font bytes. Follow the library’s font-embedding example rather than assuming built-in fonts can represent every character set.
Print HTML and CSS with Puppeteer
Choose Puppeteer when the desired PDF is the print rendering of a browser page. Its documented flow is to launch a browser, open a page, navigate to a URL, call page.pdf(), and close the browser. The PDF guide displayed version 25.12.0 when accessed on September 29, 2026; check the current documentation for the version matching your installed package.
Runnable Node.js example
Install Puppeteer with npm install puppeteer, then save this as an ES module:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle0' });
await page.pdf({ path: 'page.pdf', format: 'A4' });
} finally {
await browser.close();
}
Page.pdf() prints using print CSS media and waits for fonts to load by default, according to the guide. That makes print-specific CSS—such as page breaks, margins, and content visibility—part of the PDF output. If your application has a local HTML template, load that page in the browser instead of navigating to a public URL, and ensure required assets are reachable before printing.
Practical selection and implementation checks
Think in terms of document ownership
- Application owns every element: PDFKit is a natural fit when code constructs text, geometry, tables, images, and forms.
- PDF file is the thing being transformed: pdf-lib is the closer fit for page manipulation, embedded pages, or form work.
- Web page owns the layout: Puppeteer is appropriate when the screen/print HTML is the source of truth.
Account for output shape
PDFKit in Node writes through streams, which works well with file and HTTP destinations; ensure the stream lifecycle is completed. pdf-lib examples serialize a document with save(), yielding bytes that your application then stores or sends. Puppeteer returns a browser-generated PDF from page printing. These differing interfaces affect how you connect generation to storage, HTTP responses, and job queues.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchRank #4
Validate what readers receive
- Check page count, page size, margins, page breaks, and long-content behavior using representative documents.
- Include real images, fonts, and non-ASCII text in validation; missing assets or unsupported glyphs may not be apparent in a minimal sample.
- For browser printing, check the print-media appearance rather than relying only on the normal screen view.
- For PDF editing, test source documents from the range your application accepts, including empty or unusual page structures where relevant.
- If performance, memory, or concurrency matters, measure that workload in the target runtime; official descriptions do not establish a fair cross-library winner.
Troubleshoot common PDF generation failures
The output file is empty, incomplete, or unreadable
With PDFKit, verify that the document is piped to a writable destination and that doc.end() is called after all content is added. With pdf-lib, await save() and write the returned bytes. With Puppeteer, await page.pdf() before closing the browser.
Images or fonts are missing
Check that asset paths resolve in the runtime where generation occurs. The PDFKit browser build cannot directly access filesystem paths; supply registered bytes for those assets. For pdf-lib custom fonts, use the documented fontkit integration. With Puppeteer, ensure the page has loaded the resources it needs before printing; its PDF method waits for fonts by default, but that does not make an unavailable font file reachable.
The printed page looks different from the browser preview
Puppeteer’s PDF method uses print CSS media. Add or adjust print-specific styles and inspect page breaks, hidden elements, and dimensions in the PDF output. The guide says fonts are awaited by default, so investigate missing network assets or CSS differences if typography still changes.
The code cannot load the PDF or an asset
Confirm the input file exists, the path is valid for the Node process, and the process has permission to read it. For browser execution, replace filesystem-only assumptions with bytes available to the browser. For pdf-lib, confirm the input is a supported PDF and that loading completes before editing.
Best Value
ScreenshotNeo is not a PDF-generation SDK
ScreenshotNeo is a website screenshot API and MCP server, not a replacement for PDFKit, pdf-lib, or Puppeteer’s PDF printing. It is relevant if your task is to capture a webpage as an image or PDF without maintaining your own browser-capture setup. See ScreenshotNeo for the service overview.
Or skip the browser setup
One GET request can return a screenshot as PNG, JPEG, or WebP, or a PDF. For a webpage PDF, use format=pdf:
curl -G "https://api.screenshotneo.com/v1/shot"
-d access_key=YOUR_API_KEY
--data-urlencode url=https://example.com
-d format=pdf
-o page.pdf
Parameter names used by other screenshot APIs also work. The ScreenshotNeo API documentation describes its request options. Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, with response headers indicating the page verdict and billing status. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Sign up free for 1,000 screenshots a month, with no card required.
Frequently asked questions
Can I use these libraries from TypeScript?
Yes. pdf-lib says it is written in TypeScript and compiled to pure JavaScript. PDFKit and Puppeteer are JavaScript packages commonly used from TypeScript projects; check the current package documentation and type support for the versions you install.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Can I turn a webpage into a PDF without rebuilding its layout?
Yes. Puppeteer’s Page.pdf() prints a browser page and is the documented option here for HTML/CSS layouts. ScreenshotNeo can also return a webpage PDF from its screenshot API when you prefer a hosted capture request.
Which option should I choose for merging or splitting PDFs?
pdf-lib’s official overview documents splitting and merging as supported use cases, alongside page insertion, removal, and embedding.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




