Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Set paper size, margins, color, and page ranges

Pass output choices to page.pdf(options). The options reference includes these controls:

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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. Call page.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-adjust in 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: true when the CSS @page size 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 finally block.
  • 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.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

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:

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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import { 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.