To generate a PDF with Node.js and Puppeteer, launch Chromium, open a page, load a URL or HTML, and call page.pdf(). Puppeteer uses print CSS by default, so choose the media type and paper settings before saving the file, then close the browser. The example below writes an A4 PDF to disk.
Generate a PDF from a webpage
Install Puppeteer in a Node.js project, then create a script such as make-pdf.mjs. This ES module example follows Puppeteer’s documented launch, navigation, PDF, and close flow.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.pdf({
path: 'output.pdf',
format: 'A4',
printBackground: true,
margin: { top: '20mm', right: '15mm', bottom: '20mm', left: '15mm' }
});
} finally {
await browser.close();
}
Install the package with npm install puppeteer, then run node make-pdf.mjs. Puppeteer’s installation documentation is at pptr.dev/guides/installation, and the PDF guide documents the core sequence at pptr.dev/guides/pdf-generation.
networkidle2 waits for network activity to settle according to Puppeteer’s navigation condition; it is not proof that every application-specific element is ready. If a page renders key content after navigation, wait for a selector or another condition the site exposes before calling page.pdf().
Free tools Windows power users keep installed
One-click scans. No signup required.
#1 Best Overall
Generate a PDF from prepared HTML
When the content is generated by your application, set it directly rather than navigating to a public URL. This gives the script control over the markup and styles. Replace the example HTML with your template and ensure any linked assets are accessible to Chromium.
import puppeteer from 'puppeteer';
const html = `
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
@page { size: A4; margin: 18mm; }
body { font: 12pt Arial, sans-serif; }
h1 { break-after: avoid; }
</style>
</head>
<body><h1>Invoice</h1><p>Generated from HTML.</p></body>
</html>
`;
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.setContent(html, { waitUntil: 'networkidle0' });
await page.pdf({ path: 'invoice.pdf', printBackground: true, preferCSSPageSize: true });
} finally {
await browser.close();
}
page.setContent() sets the page content; preferCSSPageSize makes a CSS @page size take priority over PDF width, height, or format settings. See the PDFOptions reference for the current option definitions.
Choose print CSS or screen CSS
page.pdf() renders using the print CSS media type by default. Print styles can hide navigation, change spacing, or rearrange content, so a PDF may differ from the page seen in a browser window. To use screen styles, explicitly emulate the screen media type before generating the PDF:
await page.emulateMediaType('screen');
await page.pdf({ path: 'screen-styled.pdf', printBackground: true });
Use print media when the document is intended to be printed and the site provides appropriate print CSS. Choose screen media when preserving the on-screen presentation is more important. Puppeteer’s media behavior is described in its Page.pdf() API documentation.
Set paper size, margins, color, and page ranges
Pass output choices to page.pdf(options). The options reference includes these controls:
Rank #2
| Option | What it controls |
|---|---|
path |
Where Puppeteer writes the PDF file. If omitted, the API returns a buffer instead of saving to the supplied path. |
format |
A named paper size such as A4. |
width and height |
Explicit page dimensions when a named format is not the right fit. |
margin |
Top, right, bottom, and left page margins. |
landscape |
Whether pages use landscape orientation. |
printBackground |
Whether to include background graphics and colors. |
pageRanges |
Which pages to include in the output. |
preferCSSPageSize |
Whether CSS @page dimensions take priority over the PDF option dimensions or format. |
displayHeaderFooter |
Whether to display a header and footer. |
headerTemplate and footerTemplate |
Markup templates for the header and footer. |
For example, use landscape: true for a wide report, or pageRanges: '1-3' to emit only its first three pages. Header and footer templates can use injected classes for the date, title, URL, page number, and total page count; consult the PDFOptions reference for supported details and syntax.
When the page itself declares paper dimensions with CSS @page, use preferCSSPageSize: true to prioritize that sizing. Avoid specifying conflicting paper models unless you deliberately want the PDF options to take precedence.
Wait for fonts and other page assets
Puppeteer states that Page.pdf() waits for fonts to be loaded by default. That does not make unreachable external assets available: fonts, images, stylesheets, and scripts still need to load successfully in Chromium. Make sure external resources are accessible in the runtime environment and that the page has reached the state your document requires before creating the PDF.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Printed colors may be adjusted for print output. If exact colors matter, set -webkit-print-color-adjust in the page’s CSS, for example:
* {
-webkit-print-color-adjust: exact;
}
Check the resulting PDF when layout fidelity matters: CSS intended for a continuous screen can paginate differently, and color behavior can vary with the print rendering path. Puppeteer’s PDF guide and API reference explain the print media behavior and font waiting at pptr.dev/guides/pdf-generation and pptr.dev/api/puppeteer.page.pdf.
Rank #3
- hole punched
- high quality card stock
- 4 pages
- made in USA
- keyboard shortcuts
Save to a path, return a buffer, or stream output
Use path when the job should write directly to a file. Without a path, page.pdf() returns PDF data that your application can pass to another function or response. When a readable stream better fits the application’s output flow, Puppeteer also provides page.createPDFStream(options); see the createPDFStream API reference.
Choose the output approach based on what consumes the document: a path for local files, returned data for code that needs a buffer, or a stream where incremental reading is useful. A stream changes how the output is handled; it does not remove the need to wait for the page content and assets that the PDF should contain.
Handle the browser lifecycle safely
The try/finally structure ensures the browser is closed even if navigation or PDF generation throws an error. If your application handles multiple jobs, decide deliberately whether it will launch and close Chromium for every job or manage a browser process across jobs. Concurrency and isolation are application-level decisions: consider whether pages may contain private user data, and avoid sharing page state between unrelated jobs.
Do not omit cleanup in a long-running process. An unclosed browser can leave Chromium processes running after a failed capture. For repeated jobs, handle each page’s errors and cleanup explicitly, and test the lifecycle in the environment where the script will run.
Troubleshoot common PDF problems
- The PDF looks different from the screen:
page.pdf()uses print CSS by default. Callpage.emulateMediaType('screen')before generating the PDF if screen styles are intended. - Background colors or images are missing: set
printBackground: true. For color fidelity, apply-webkit-print-color-adjustin the page CSS. - The page is blank or missing late-rendered content: navigation completion and application readiness are different conditions. Wait for a meaningful selector or application state before calling
page.pdf(). - A custom font or image is absent: confirm the resource URL is reachable from the Chromium process and that the page has finished loading the content. Fonts are awaited by default during PDF generation, but that cannot compensate for failed requests.
- The paper size does not match the CSS: use
preferCSSPageSize: truewhen the CSS@pagesize should win, or remove conflicting sizing declarations. - The script fails before writing a file: check that Chromium can launch in the target environment, the destination directory is writable, and the navigation or PDF call’s error is not being swallowed. Keep browser cleanup in a
finallyblock. - The PDF has unexpected pagination: inspect print-specific styles and page dimensions; content designed for a continuous viewport may break across sheets differently. Adjust the document’s print CSS and page margins.
Or skip the browser setup
If you need a screenshot or PDF from a URL without managing a Puppeteer browser, ScreenshotNeo offers a single-request API. Its PDF options include paper size, margins, landscape orientation, and page ranges. This Node.js example makes the request and writes the response body to a file:
Rank #4
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.pdf', res);
Use the API’s PDF parameter options and response details in the ScreenshotNeo documentation. For a plain Node.js runtime without Bun, write the response bytes with Node’s file system API:
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows 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 reinstallimport { writeFile } from 'node:fs/promises';
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await writeFile('shot.pdf', Buffer.from(await res.arrayBuffer()));
Set the PDF-related parameters required by your use case according to the API documentation. ScreenshotNeo accepts cookie and consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and responses identify the page verdict and billing status in headers. Its 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 per month with no card; paid plans start at $5 for 3,000 shots. Learn more at ScreenshotNeo, or sign up free.
Performance, reliability, and cost considerations
The Puppeteer guide does not establish a generation-time benchmark, so actual runtime depends on the page, network, assets, and environment. The choice to create a new browser for every job versus reuse a managed process affects lifecycle and resource planning, but there is no universal concurrency setting in the PDF API reference. Measure with the pages and runtime you actually intend to serve.
For reliability, treat navigation, readiness, PDF generation, output storage, and browser cleanup as separate failure points. Log the failing stage, set appropriate application-level timeouts, and keep sensitive page data isolated when jobs run concurrently. Puppeteer itself does not set an external service price for local PDF generation; budget for the compute and operational environment in which Chromium runs.
Frequently Asked Questions
Can Puppeteer generate a PDF from HTML without opening a website URL?
Yes. Set the document with `page.setContent()` and then call `page.pdf()`.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Does `page.pdf()` wait for web fonts?
Puppeteer states that PDF generation waits for fonts to load by default.
Can I generate only selected pages of a PDF?
Yes. The `pageRanges` PDF option selects which pages to include.
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.

