ScreenshotNeo

BlogHTML to image & PDF

How to Generate a PDF of a Website With Playwright

Use Playwright’s page.pdf() to save a website as a PDF. Choose print or screen CSS, set page options, and handle output reliably.

By the ScreenshotNeo team4 October 20269 min read

Use Playwright to open the website in a browser page, then call page.pdf(). In JavaScript, await page.pdf({ path: 'page.pdf', format: 'A4' }) writes the PDF to disk. By default, PDF generation uses print CSS, omits background graphics, and uses Letter paper. Set the relevant options explicitly when the output needs a particular layout.

1. Install Playwright and its browser

Create a Node.js project and install Playwright. Its browser binaries are version-specific, so install them with the Playwright CLI and keep them aligned with the installed package.

mkdir website-to-pdf
cd website-to-pdf
npm init -y
npm install playwright
npx playwright install chromium

Save the following as website-to-pdf.js. Replace the example URL with the page you are allowed to access.

2. Generate a website PDF with Playwright

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  try {
    const page = await browser.newPage();
    await page.goto('https://example.com', { waitUntil: 'networkidle', timeout: 60000 });
    await page.pdf({
      path: 'website.pdf',
      format: 'A4',
      printBackground: true,
      margin: { top: '15mm', right: '15mm', bottom: '15mm', left: '15mm' }
    });
  } finally {
    await browser.close();
  }
})();

Run it with node website-to-pdf.js. The output path is relative to the process working directory. The API also returns a PDF buffer when path is omitted; you can then send or store those bytes yourself.

Navigation completing does not guarantee an application has finished rendering the content you need. Choose a readiness condition that matches the target page: wait for a specific selector, a known app-ready signal, or a short delay after navigation. Avoid assuming every site becomes quiet at the same time.

3. Choose print CSS or screen CSS

page.pdf() renders using print CSS media. A site’s @media print rules may hide navigation, change typography, or reflow content. The Playwright Page API states that “page.pdf() generates a pdf of the page with print css media.” If the PDF should match the screen layout, emulate screen media before generating it:

await page.emulateMedia({ media: 'screen' });
await page.pdf({ path: 'website-screen-layout.pdf', format: 'A4', printBackground: true });

Use print media for a document intended for paper or a print-friendly reading layout. Use screen media when the site’s screen-specific composition is the desired result. Inspect pages with both media settings if the site has substantial print styles.

4. Configure page size, margins, colors, and output

Option What it controls Important behavior
format Preset paper size, such as A4 or Letter. Defaults to Letter. When supplied, it takes precedence over width and height.
width, height Custom paper dimensions. Accept values with units such as px, in, cm, or mm. Bare numbers are pixels.
margin Top, right, bottom, and left paper margins. Defaults to no margins. Set each side when predictable printable spacing matters.
preferCSSPageSize Whether CSS @page size controls the PDF paper size. Set to true to prioritize the page’s CSS size. Otherwise content is scaled to fit the selected paper.
printBackground Background graphics and colors. Defaults to false. Set to true to include them.
scale Scales page content. Defaults to 1; accepted range is 0.1 to 2.
pageRanges Pages to include, such as 1-5, 8. Useful for extracting selected pages from a long PDF.
path Destination file. Relative paths resolve from the current working directory. Without it, the call returns a buffer.
displayHeaderFooter Whether to include header and footer templates. Use with headerTemplate and footerTemplate.
tagged, outline Tagged PDF and document outline output. Both options were added in Playwright v1.42 and default to false.

For example, to use a CSS-defined paper size and return bytes instead of writing a file:

const pdfBuffer = await page.pdf({
  preferCSSPageSize: true,
  printBackground: true,
  margin: { top: '10mm', bottom: '10mm', left: '12mm', right: '12mm' },
  pageRanges: '1-5'
});

To force exact print colors, the Playwright API documentation points to the CSS property -webkit-print-color-adjust. Add an appropriate rule to the page or inject it where permitted, then check the result for the target site:

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

5. Add headers and footers

Header and footer templates are HTML strings. Template scripts are not evaluated, and page styles are not visible inside the templates, so provide the necessary inline styles and use the supported template placeholders.

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

Reserve enough top and bottom margin for the templates. Keep them simple; scripts in these templates will not run.

6. Handle readiness, output, and failures

For a site whose main content appears after navigation, wait for that content before printing. This example uses a selector and writes the returned buffer explicitly:

const fs = require('node:fs/promises');
const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  try {
    const page = await browser.newPage();
    await page.goto('https://example.com/article', { waitUntil: 'domcontentloaded', timeout: 60000 });
    await page.locator('main article').waitFor({ state: 'visible', timeout: 30000 });
    const pdf = await page.pdf({ format: 'A4', printBackground: true });
    await fs.writeFile('article.pdf', pdf);
  } finally {
    await browser.close();
  }
})();

Use try/finally so the browser closes if navigation, waiting, or PDF generation throws. In a service that processes many URLs, also record the URL and failure stage, enforce a job timeout, and avoid sharing one mutable page between concurrent jobs.

7. cURL, Python, and Node.js alternatives

These examples show the same website-to-PDF workflow using Playwright. Playwright’s official setup and API documentation cover browser installation and the JavaScript API; the Python example uses Playwright’s Python binding.

Node.js

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  try {
    const page = await browser.newPage();
    await page.goto('https://example.com', { waitUntil: 'networkidle', timeout: 60000 });
    await page.pdf({ path: 'website.pdf', format: 'A4', printBackground: true });
  } finally {
    await browser.close();
  }
})();

Python

from pathlib import Path
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    try:
        page = browser.new_page()
        page.goto("https://example.com", wait_until="networkidle", timeout=60000)
        pdf_bytes = page.pdf(format="A4", print_background=True)
        Path("website.pdf").write_bytes(pdf_bytes)
    finally:
        browser.close()

Install the Python package and browser binaries with:

pip install playwright
playwright install chromium

cURL

Playwright is a browser automation library, not an HTTP endpoint, so cURL cannot call page.pdf() directly. You can use cURL to download a PDF that a website already serves, but that does not render a web page as a PDF. To generate one with Playwright, run the Node.js or Python code above in an environment with Playwright and its browser installed.

8. Browser choice and installation

Playwright supports Chromium, WebKit, and Firefox for browser automation, but PDF generation behavior depends on the API and browser used. Use the browser and binding documented for your workflow, and install the matching browser binaries with the CLI. Playwright recommends updating the package and reinstalling browser binaries as versions change. For Chrome or Edge automation through Playwright’s Chromium support, its BrowserType documentation says compatibility works best with the Chromium version bundled with Playwright; compatibility with other browser versions is not guaranteed.

The Playwright MCP PDF export page describes that MCP tool’s PDF generation as Chromium-only. That scope applies to the MCP export feature; it should not be generalized to every Playwright API or binding.

9. Troubleshooting

Symptom Likely cause Fix
Browser launch fails because an executable is missing. The matching browser binary has not been installed, or the package and browser versions are out of sync. Run npx playwright install chromium (or the equivalent CLI for your binding), and update/reinstall the browsers with the Playwright package.
Navigation times out. The site is slow, unreachable, or keeps network activity open. Use a suitable navigation condition such as domcontentloaded, increase the timeout where justified, then wait for the specific content you need. Do not treat a timeout increase as proof the page is ready.
PDF is blank or missing content. The app renders content after navigation, or the readiness condition completed too early. Wait for a visible content selector or an application-specific ready signal before calling pdf().
Colors, images, or backgrounds are absent. Background printing is off by default, or the site uses print styles that alter appearance. Set printBackground: true; consider screen media if appropriate. For exact print colors, use -webkit-print-color-adjust: exact and inspect the result.
Content is clipped or scaled unexpectedly. The chosen paper format, dimensions, margins, or CSS @page size do not match the content. Choose format or explicit dimensions, adjust margins, and set preferCSSPageSize: true when CSS page sizing should take priority.
The output file cannot be found. A relative path is resolved from the process working directory, which may differ from the script directory. Use an absolute path or construct one from the script’s directory; alternatively, omit path and handle the returned buffer.
Header or footer is missing or unstyled. displayHeaderFooter is false, margins leave no room, or the template relies on page styles or scripts. Enable the option, reserve margin space, and put needed styles directly in the template. Template scripts do not execute.

10. Performance, reliability, and cost

PDF creation requires launching or reusing a browser process, loading the page, waiting for its content, and rendering the document. A simpler readiness condition can reduce unnecessary waiting, while too-early printing risks incomplete output. For repeated jobs, browser reuse can avoid repeated startup work, but isolate pages and contexts between unrelated jobs and close resources when finished.

Page size, print backgrounds, and the amount of page content affect output size and rendering work. Set a timeout appropriate to the workload, capture errors per URL, and retry only failures that may be transient. A retry cannot fix a permanently inaccessible page or a selector that does not exist. Playwright itself is software you run; cost depends on the machine or hosted environment where the browser runs. The cited Playwright documentation does not provide a general speed benchmark or a fixed operating cost.

Or skip the browser setup

ScreenshotNeo can return a PDF from one GET request, with options for PDF paper size, margins, landscape orientation, and page ranges. It also accepts custom headers and cookies for pages that need them. Read the ScreenshotNeo API documentation for request options.

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

For this house product, cookie and consent 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, and responses report the page verdict and billing status in headers. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Create a free ScreenshotNeo account to get 1,000 screenshots per month with no card.

FAQ

How do I save a webpage as a PDF in Playwright?

Navigate a page, then call page.pdf({ path: 'page.pdf' }) in JavaScript. Without a path, the call returns PDF bytes.

How do I print a page with Playwright?

Call page.pdf(). It uses print CSS media by default; use page.emulateMedia({ media: 'screen' }) first when you need screen CSS.

How do I include background colors in a Playwright PDF?

Set printBackground: true. For exact print colors, the API documentation points to CSS -webkit-print-color-adjust.

Can I create only selected pages?

Yes. Set pageRanges, for example '1-5, 8', to select ranges.

Official references