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.

Use Puppeteer’s page.pdf() method after loading or building your document. Choose paper dimensions, margins, print CSS, backgrounds, headers and footers, then save the returned PDF bytes or write them directly to a file. The complete workflow below handles multi-page layouts, custom page sizes, dynamic content, fonts, page ranges and common failures.

What Puppeteer uses to create a PDF

Puppeteer renders the current page through Chromium’s print pipeline. Page.pdf() returns a Promise<Uint8Array>; supplying path writes the result to disk. The official guide’s basic flow navigates with waitUntil: 'networkidle2', calls page.pdf(), and closes the browser, but that network event is an example rather than a guarantee that every application has finished rendering.

By default, PDF rendering uses print CSS media. Consequently, a page can look different from its screen view. Call page.emulateMediaType('screen') when screen styles are required, or create a dedicated @media print stylesheet for a document designed for paper. See the Puppeteer PDF generation guide and the Page.pdf() API reference.

Complete multi-page example

Install Puppeteer in a Node.js project, then create a script such as generate-pdf.mjs. This example loads a URL, waits for the page’s own content, applies print settings, and produces an A4 PDF.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
npm install puppeteer
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,
    preferCSSPageSize: true,
    margin: {
      top: '18mm',
      right: '16mm',
      bottom: '18mm',
      left: '16mm'
    }
  });
} finally {
  await browser.close();
}

This is a documented API pattern; adapt the wait condition and page preparation to the site you are rendering. format selects a standard paper size. If you specify format, it takes precedence over width and height. With preferCSSPageSize: true, CSS @page dimensions take priority over those JavaScript options. The API defaults to no margins and printBackground: false, so set both deliberately when the document needs them. Details are in the PDFOptions reference.

Prepare content before calling page.pdf()

Navigate and wait for application rendering

page.goto() resolves according to the selected lifecycle event, not according to your application’s data model. Single-page applications may still be fetching data, images or charts after navigation. Wait for a selector that proves the report is ready, use a known delay only when necessary, or wait for an application-defined promise.

await page.goto('https://example.com/report', { waitUntil: 'domcontentloaded' });
await page.waitForSelector('#report-ready');

For pages you generate yourself, page.setContent(html, { waitUntil: 'load' }) is often simpler. You can also call page.evaluate() to insert data or trigger rendering before creating the PDF.

Fonts and images

Puppeteer’s PDF method waits for fonts by default; PDFOptions.waitForFonts defaults to true. It still helps to make font loading explicit when a layout is sensitive to font metrics:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.evaluate(() => document.fonts.ready);
await page.waitForSelector('img.report-chart');

Use absolute, reachable asset URLs or embed assets as data URLs. If an image is lazy-loaded, scroll it into view or wait until its complete property is true before printing.

Control page size, orientation and margins

Standard paper

Use format: 'A4', 'Letter' or another supported standard. Add landscape: true for a horizontal report. Do not combine a format with custom width and height expecting the custom values to win.

Custom CSS page sizes

Put document dimensions and margins in your stylesheet when the layout owns those decisions:

@page {
  size: A4 portrait;
  margin: 18mm 16mm;
}

@media print {
  .screen-only { display: none !important; }
  .avoid-break { break-inside: avoid; }
  h1, h2 { break-after: avoid; }
}

Then pass preferCSSPageSize: true. CSS page-break rules influence pagination, but no single rule works for every table, chart and nested flex layout. Inspect the generated PDF and adjust the print stylesheet rather than relying on screen layout alone.

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

Print colors, backgrounds and headers

Background graphics and exact colors

Set printBackground: true to include background fills and images. Print rendering can adjust colors; the Puppeteer API notes that CSS -webkit-print-color-adjust can request exact color treatment:

@media print {
  * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}

Page headers and footers

Enable displayHeaderFooter and provide HTML templates. Puppeteer substitutes classes such as date, title, url, pageNumber and totalPages.

await page.pdf({
  path: 'report.pdf',
  format: 'A4',
  displayHeaderFooter: true,
  headerTemplate: '<div style="font-size:8px;width:100%;text-align:center;">Quarterly report</div>',
  footerTemplate: '<div style="font-size:8px;width:100%;text-align:center;">Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>',
  margin: { top: '24mm', bottom: '20mm', left: '16mm', right: '16mm' }
});

Header and footer templates have their own constrained layout. Reserve enough top and bottom margin or the content can overlap them.

Save all pages or a selected range

Without path, use the returned bytes in an HTTP response, object store or database:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const pdfBytes = await page.pdf({ format: 'A4' });
// Example: return Buffer.from(pdfBytes) from your web handler

To export only selected pages, use pageRanges, for example '1-5, 8, 11-13'. Page numbering starts at one. Confirm the resulting page count when content is dynamic; a range that exceeds the document’s length may fail or produce an unexpected result.

Generate a PDF from HTML instead of a URL

This pattern is useful for invoices, reports and emails assembled by your server:

import puppeteer from 'puppeteer';

const html = `<!doctype html>
<html><head>
<style>
@page { size: A4; margin: 15mm; }
body { font-family: Arial, sans-serif; }
table { width: 100%; border-collapse: collapse; }
td, th { border: 1px solid #ccc; padding: 6px; }
</style>
</head><body>
<h1>Invoice</h1>
<p>Generated at ${new Date().toISOString()}</p>
</body></html>`;

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.setContent(html, { waitUntil: 'load' });
  await page.pdf({
    path: 'invoice.pdf',
    printBackground: true,
    preferCSSPageSize: true
  });
} finally {
  await browser.close();
}

Escape or sanitize any untrusted values inserted into HTML. A PDF job that accepts arbitrary markup can otherwise become an injection or data-access risk.

Options that matter in production

  • printBackground: include CSS backgrounds and graphics; default is false.
  • preferCSSPageSize: let @page dimensions override JavaScript paper settings; default is false.
  • margin: set explicit top, right, bottom and left values; the default is no margin.
  • displayHeaderFooter, headerTemplate, footerTemplate: add repeated page furniture.
  • landscape: rotate standard paper for wide tables.
  • pageRanges: export a subset such as 1-3, 7.
  • waitForFonts: retain the default true when font metrics affect wrapping.
  • path: write directly to a file; omit it to receive PDF bytes.

Troubleshooting common failures

The PDF is blank or missing content

The capture probably ran before the application rendered. Replace a generic network wait with waitForSelector, an application-ready flag or an explicit data-loading wait. Verify that the target route works in the same Chromium environment and that authentication cookies are present.

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

Styles or colors differ from the browser

Print media is the default. Add print rules, call page.emulateMediaType('screen') when screen CSS is intended, and enable printBackground. For brand colors, use -webkit-print-color-adjust: exact and check the output.

Content is clipped or unexpectedly scaled

Check CSS @page, margins and the interaction between format, width, height and preferCSSPageSize. Wide tables often need landscape orientation or responsive print rules. Avoid fixed-height containers that hide overflow.

Fonts wrap differently or fallback fonts appear

Wait for document.fonts.ready, keep waitForFonts enabled, and ensure the browser can reach the font files. Cross-origin restrictions, blocked requests and incorrect MIME types can all cause fallback fonts.

Headers overlap the body

Increase the corresponding top or bottom margin. Header and footer templates do not automatically create usable space for the main document.

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

Navigation never finishes

networkidle2 can remain unsettled on pages with analytics, WebSockets or long polling. Use domcontentloaded followed by a selector or application-specific readiness check, and block or mock nonessential requests when appropriate.

The process is slow or runs out of memory

Reuse a browser process for batches, close each page in a finally block, avoid unnecessarily huge viewport dimensions, and limit concurrent jobs. Large images and very long pages consume substantial memory; resize assets and split exceptionally large reports when the document design permits.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Version and browser compatibility

The Puppeteer documentation search identifies version 25.12.0 and pairs it with Chrome for Testing 154.0.8037.57 and Firefox 156.0.1. These values are version-specific and can change; check the supported browsers page and your installed package before pinning a deployment. Puppeteer switched to Chrome for Testing beginning with v20.0.0.

Or skip the browser setup

If you need an HTTP screenshot or PDF service rather than maintaining Chromium workers, ScreenshotNeo accepts one GET request for a URL and can return a PDF. Its cleanup steps accept cookie and consent banners and remove more than 60 known consent platforms, newsletter popups and chat widgets before capture; each step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing result.

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

Use the API documentation at https://screenshotneo.com/docs/ for authentication and options. A minimal call is:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.pdf

ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients. The free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to get started.

Frequently Asked Questions

Can Puppeteer create a PDF without visiting a URL?

Yes. Call page.setContent() with your HTML, wait for required assets, and then call page.pdf().

Does page.pdf() include backgrounds automatically?

No. Set printBackground: true; its default is false.

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

How can I use CSS-defined paper dimensions?

Declare an @page rule and pass preferCSSPageSize: true.

Can I return the PDF from an API endpoint?

Yes. Omit path, receive the returned Uint8Array, and send it with a PDF content type.

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.