ScreenshotNeo

BlogHow-to

How to Capture a Website Screenshot with Playwright and Save It as a PDF

Capture a full-page image and export a PDF with Playwright. Learn the options, layout controls, troubleshooting steps, and a no-browser-setup alternative.

By the ScreenshotNeo team4 October 20268 min read

Use Playwright’s Page API: navigate to the page, call page.screenshot() to save an image, and call page.pdf() separately to save a PDF. Set fullPage: true when the image should include the full scrollable page. PDF output uses print CSS by default; emulate screen media first if you want the page’s screen styling.

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

(async () => {
  const browser = await chromium.launch();
  try {
    const page = await browser.newPage({
      viewport: { width: 1440, height: 900 },
      deviceScaleFactor: 1,
    });

    await page.goto('https://example.com', { waitUntil: 'networkidle' });
    await page.screenshot({ path: 'screenshot.png', fullPage: true });
    await page.pdf({ path: 'page.pdf', format: 'A4', printBackground: true });
  } finally {
    await browser.close();
  }
})();

The example uses Chromium, which is the standard choice for this workflow. Install Playwright and its browser before running the script:

npm init -y
npm install playwright
npx playwright install chromium
node capture.js

Save the JavaScript as capture.js. Playwright’s Page API reference documents screenshot and PDF options; its screenshots guide shows viewport and full-page captures.

1. Choose image, PDF, or both

page.screenshot() produces an image; page.pdf() produces a paginated document. They are separate calls with different options. A full-page screenshot is one tall image of the scrollable page. A PDF lays content out across pages according to print styles and PDF settings.

Need Use Key setting
Image of the current viewport page.screenshot() Omit fullPage or set it to false
One tall image of the page page.screenshot() fullPage: true
Printable, paginated document page.pdf() Choose page size, margins, and print options
Image and document files Call both methods Configure each output independently

For the image, path writes the file; without it, the method returns an image buffer. Documented formats include PNG, JPEG, and WebP. For PDF, path writes the file and the method also returns a buffer. See the Page API for version-specific details.

2. Wait for the page to reach the state you want

A capture records the page’s current rendered state. Choose a navigation wait condition that fits the site, then wait for any important content or application state before capturing.

await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.locator('main').waitFor({ state: 'visible' });
await page.screenshot({ path: 'main.png', fullPage: true });

networkidle can be convenient for pages that settle after network requests, but analytics, polling, or other persistent requests may prevent it from becoming idle. In that case, wait for a meaningful selector or application-specific condition. A fixed delay can help with known delayed rendering, but it is less reliable than waiting for the content you need.

3. Capture a full-page image

Set fullPage: true to capture the full scrollable page instead of only the viewport. The browser expands the capture to include content below the fold. For a viewport image, leave this option out.

await page.screenshot({
  path: 'full-page.webp',
  type: 'webp',
  fullPage: true,
});

Set the format to PNG, JPEG, or WebP when needed; if you choose JPEG, consider a quality value appropriate to the page. Other screenshot settings are documented in the Page API. Full-page images can become very large for long pages. If the goal is page-based sharing or printing, PDF pagination may be more practical.

4. Save a PDF and control its layout

PDF generation uses print CSS media by default. This means print-specific styles can change what appears compared with a normal browser view. To render with screen media instead, call page.emulateMedia({ media: 'screen' }) before page.pdf().

await page.emulateMedia({ media: 'screen' });
await page.pdf({
  path: 'page-screen-style.pdf',
  format: 'A4',
  printBackground: true,
  margin: {
    top: '12mm',
    right: '12mm',
    bottom: '12mm',
    left: '12mm',
  },
  preferCSSPageSize: true,
});

Common layout controls include:

  • format: paper preset such as A4 or Letter. The documented default is Letter; verify the API reference for the installed Playwright version.
  • width and height: set page dimensions directly when a preset is not suitable.
  • margin: set top, right, bottom, and left page margins.
  • printBackground: include background graphics in the PDF.
  • landscape: use landscape orientation.
  • pageRanges: export selected pages, using the syntax documented for your installed version.
  • scale: adjust the rendered content size.
  • preferCSSPageSize: prefer page sizing declared in CSS.

For CSS-defined page dimensions, use @page rules and consider preferCSSPageSize: true. PDF printing can alter colors by default; the API reference describes using CSS -webkit-print-color-adjust to preserve exact colors. See the PDF API options for the current option names and behavior.

5. A complete reusable script

This script accepts a URL, captures both outputs, and closes Chromium even if navigation or writing fails. It sets a finite navigation timeout and waits for the main content to appear. Adjust the selector and output choices for the target page.

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

async function capture(url) {
  const browser = await chromium.launch();
  try {
    const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
    page.setDefaultNavigationTimeout(30_000);

    await page.goto(url, { waitUntil: 'domcontentloaded' });
    await page.locator('body').waitFor({ state: 'visible' });

    await page.screenshot({
      path: 'page.png',
      fullPage: true,
      animations: 'disabled',
    });
    await page.pdf({
      path: 'page.pdf',
      format: 'A4',
      printBackground: true,
      margin: { top: '12mm', right: '12mm', bottom: '12mm', left: '12mm' },
    });
  } finally {
    await browser.close();
  }
}

const url = process.argv[2];
if (!url) {
  console.error('Usage: node capture.js https://example.com');
  process.exitCode = 2;
} else {
  capture(url).catch((error) => {
    console.error(error);
    process.exitCode = 1;
  });
}

Run it with:

node capture.js https://example.com

The animations: 'disabled' screenshot option can help avoid capturing an element midway through an animation. The API also documents screenshot options such as masking. Pick options based on the state you want recorded.

6. Configure the browser and output deliberately

Viewport and device scale

The viewport controls the page’s layout width and height. Set it before navigation or capture when you need consistent responsive behavior. A larger device scale factor can produce denser raster output, at the cost of more pixels and memory. These settings affect screenshot rendering; PDF page size and print layout are controlled separately.

Page state and access

For pages that require authentication or specific state, establish that state before capture using the browser context and page APIs. Avoid placing secrets in source code or logs. If the page depends on a cookie banner choice or a particular interaction, perform it before taking the capture.

Output path and buffers

Use a distinct path for each output, and ensure the process can write to its parent directory. Omitting path returns a buffer, which is useful when sending output to storage or another service instead of writing locally. Be mindful that full-page images and returned buffers occupy memory until released.

7. Troubleshooting

Symptom Likely cause Fix
Browser executable missing Playwright package is installed but Chromium was not installed in this environment. Run npx playwright install chromium in the deployment environment.
Navigation times out The page is slow, keeps requests open, or the chosen wait condition never occurs. Use a suitable waitUntil condition such as domcontentloaded, set a deliberate timeout, and wait for the content selector you need.
Capture is blank or missing content The app renders after navigation, or content is gated on interaction or data loading. Wait for a visible selector or application-specific ready state before capture.
PDF looks different from the browser PDF uses print media by default, and print CSS may change layout or visibility. Use print styles intentionally, or call page.emulateMedia({ media: 'screen' }) before generating the PDF.
PDF backgrounds or colors are missing Background printing is disabled or print color adjustment changes colors. Set printBackground: true; use the documented CSS print color adjustment when exact colors are required.
Output file is absent The destination directory does not exist or is not writable. Create the directory and check the process’s filesystem permissions and working directory.
Very tall capture consumes too much memory A long page produces a large image or buffer. Capture a viewport, a specific portion, or a paginated PDF instead; avoid keeping unnecessary buffers alive.
PDF page size ignores CSS The configured paper format takes precedence over the document’s declared page size. Review the @page styles and set preferCSSPageSize: true when CSS dimensions should control sizing.

8. Performance, reliability, and cost

Browser capture requires launching or reusing a browser process, loading the target page, rendering it, and writing image or PDF data. Reusing a browser can avoid repeated launch work in a long-running service, but isolate jobs with separate contexts when they should not share state. Close pages, contexts, and browsers when finished.

Use explicit timeouts and wait for the minimum state required by the capture. Waiting for all network activity can be slow or impossible on sites with ongoing requests. For repeatable output, set the viewport, media mode, and relevant page state consistently. Dynamic ads, personalized content, and changing site data can still make captures differ between runs.

Playwright itself is the browser automation library; this workflow has no ScreenshotNeo per-capture charge. Operational cost depends on where and how often the browser runs, and on CPU, memory, storage, and execution time. A hosted screenshot API may be simpler when you do not want to provision browsers.

9. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request can return an image or PDF. Its capture options include full-page screenshots, viewport and device presets, PDF layout controls, custom CSS and JavaScript, selector waits, and more. See the ScreenshotNeo API documentation for parameters.

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

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
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 require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo accepts cookie and consent banners like 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, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf 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 screenshots; every feature is available on every plan. Sign up free and get 1,000 screenshots a month with no card.

10. Frequently asked questions

Does Playwright make the screenshot and PDF in one call?

No. Use page.screenshot() for the image and page.pdf() for the PDF.

Can I get the screenshot without saving it to disk?

Yes. Omit the screenshot path option and use the returned image buffer.

Does fullPage: true apply to PDF output?

No. It is a screenshot option. PDFs use paper layout and pagination settings.

Is the PDF API limited to Chromium?

The cited Chromium-only note applies to Playwright MCP’s PDF tool. It does not establish the same restriction for the Page API. Check the current Page API documentation for browser-specific behavior in the version you use.

Where can I check exact option support?

Use the Playwright Page API reference for the installed version: Page API.