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

Use Playwright’s page.screenshot() after navigating to the page. Launch a browser with headless: true (the documented default), call the method with a file path, and close the browser. Add fullPage: true for the entire scrollable page, or capture a locator for one element.

Basic headless screenshot

Install Playwright, launch Chromium without a visible window, navigate, save the image, and close the browser:

npm install playwright
npx playwright install chromium
const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch({ headless: true });
  const page = await browser.newPage();
  await page.goto('https://example.com');
  await page.screenshot({ path: 'screenshot.png' });
  await browser.close();
})();

The filename extension determines the format when you provide a path. PNG is the default when Playwright cannot infer a type. The supported screenshot types are PNG, JPEG and WebP. If you omit path, the method returns an image buffer instead of writing a file.

Choose the area to capture

Viewport screenshot

The basic call captures the page as currently laid out in the viewport. It is useful for a browser-like view, a preview card or a repeatable visual check.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.screenshot({ path: 'viewport.webp', type: 'webp', quality: 85 });

Quality applies to lossy formats such as JPEG and WebP. PNG does not use a quality setting.

Full-page screenshot

Set fullPage: true to capture the page’s full scrollable height:

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

A full-page image preserves content below the fold, but it can become very tall and harder to inspect or distribute. Use it for documentation and archival captures; use a viewport image when the visible browser context matters more.

One element

Capture a specific element through a locator. Playwright scrolls the locator into view first:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.locator('.header').screenshot({ path: 'header.png' });

Locator screenshots do not reveal pixels covered by another element. A scrollable element captures only the content currently visible inside that element, rather than automatically stitching its entire internal scroll area.

Clip a rectangle

For a fixed region of the page, pass a rectangle in CSS pixels:

await page.screenshot({
  path: 'region.png',
  clip: { x: 40, y: 80, width: 900, height: 500 }
});

Clipping is useful for stable components, but coordinates become invalid when responsive layout, fonts or content change. Prefer a locator when the target has a reliable selector.

Control viewport, scale and rendering

Set a deterministic viewport

const page = await browser.newPage({
  viewport: { width: 1440, height: 900 }
});

Keep viewport dimensions, browser version and device settings constant when comparing images. A responsive site can legitimately render a different layout at another width.

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

CSS pixels versus device pixels

The screenshot scale option controls output density:

  • scale: 'css': one image pixel per CSS pixel, producing a more compact artifact.
  • scale: 'device': device-pixel output, which can be larger on high-DPI devices and preserve more detail.
await page.screenshot({ path: 'retina.png', scale: 'device' });

Choose CSS scale for lightweight visual diffs and device scale when the consumer needs high-resolution detail. Do not mix scales between baseline and comparison images.

Freeze animations and the caret

Animations can make two otherwise identical captures differ. Disable them during capture:

await page.screenshot({
  path: 'stable.png',
  animations: 'disabled',
  caret: 'hide'
});

With animations disabled, Playwright stops CSS animations, transitions and Web Animations for the capture. The API treats finite and infinite animations differently, so a page with long-running motion should still be checked for late-loading content.

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.

Mask dynamic regions and inject styles

When a timestamp, avatar or rotating advertisement is intentionally variable, mask it or inject a screenshot-specific style. Masking should hide a known dynamic region; it should not conceal a genuine layout regression.

await page.screenshot({
  path: 'masked.png',
  mask: [page.locator('[data-testid="live-clock"]')],
  style: `video, .carousel { visibility: hidden !important; }`
});

Use a stable selector for every masked locator. If the selector stops matching, investigate the page change rather than silently broadening the mask.

Transparent backgrounds

The screenshot API can omit the default background for transparency. This is appropriate for PNG compositing; JPEG cannot represent transparency.

await page.screenshot({
  path: 'transparent.png',
  omitBackground: true
});

Waiting for the page you actually want

page.goto() starts navigation, but modern pages often continue rendering afterward. Wait for a meaningful selector before capturing:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.goto('https://example.com/dashboard', { waitUntil: 'domcontentloaded' });
await page.locator('[data-testid="dashboard"]').waitFor({ state: 'visible' });
await page.screenshot({ path: 'dashboard.png', fullPage: true });

For content that appears after a known interaction, perform that interaction first. A fixed delay can help with a page that has no reliable selector, but it is less robust than waiting for the actual state:

await page.waitForTimeout(1000); // last resort for an unobservable delay

Lazy-loaded images may not exist until scrolling triggers them. For a full-page capture, verify that important images are loaded; if necessary, scroll the page in steps before taking the screenshot and wait for each required image.

Cookies, authentication and browser context

Create a context with the locale, timezone, color scheme or device profile your screenshot represents:

const context = await browser.newContext({
  viewport: { width: 1280, height: 800 },
  colorScheme: 'dark',
  locale: 'en-US',
  timezoneId: 'America/New_York'
});
const page = await context.newPage();

For an authenticated page, use a test account or an exported storage state rather than hard-coding credentials in source. Keep secrets outside logs and commit history. If the page depends on a cookie banner, set the appropriate cookie or automate the consent action before the capture.

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

Complete reusable script

This script accepts a URL and output path, uses a fixed viewport, waits for network activity to settle, and writes a full-page WebP:

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

async function main() {
  const url = process.argv[2] || 'https://example.com';
  const output = process.argv[3] || 'page.webp';
  const browser = await chromium.launch({ headless: true });
  try {
    const context = await browser.newContext({
      viewport: { width: 1440, height: 900 },
      colorScheme: 'light'
    });
    const page = await context.newPage();
    await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
    await page.waitForLoadState('networkidle');
    await page.screenshot({
      path: output,
      fullPage: true,
      type: 'webp',
      quality: 85,
      animations: 'disabled',
      caret: 'hide'
    });
    await context.close();
  } finally {
    await browser.close();
  }
}

main().catch(error => {
  console.error(error);
  process.exit(1);
});

Run it with node capture.js https://example.com example.webp. The finally block closes Chromium even when navigation or capture fails.

Playwright Test screenshots

Manual page.screenshot() calls are best when the capture is part of your own workflow. Playwright Test can collect artifacts automatically. Configure screenshot behavior as on, only-on-failure or on-first-failure; full-page screenshots can also be enabled in the test-runner configuration. Automatic artifacts are convenient for diagnosing failed tests, while a manual call gives precise control over timing, masking, filename and format.

Visual regression reliability

Rendering can vary with the host operating system, browser version, browser settings, hardware, power source and headless mode. Generate baselines and comparisons in the same environment, ideally with a pinned Playwright/browser version and identical viewport and device settings.

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.
  • Keep the browser and operating-system image consistent.
  • Disable animations and hide carets for deterministic captures.
  • Wait for the same application state every time.
  • Mask only genuinely dynamic regions.
  • Review font availability, device scale and color scheme when a diff appears.

Playwright Test’s screenshot assertions wait until two consecutive screenshots match before comparing with the expected image. That reduces noise from a still-settling page, but it cannot correct a wrong selector, missing font or inconsistent environment.

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

Troubleshooting common failures

“Executable doesn’t exist”

Install the browser binaries with npx playwright install chromium. In a restricted CI image, ensure the required system dependencies are installed according to your Playwright setup.

Timeout during navigation

The server may be slow, blocked or waiting on a resource. Increase the navigation timeout only when the target legitimately needs more time, then wait for a specific ready selector instead of relying solely on a long delay. Capture the error and URL so failed jobs are diagnosable.

Blank or incomplete image

Capture after the application’s content selector is visible. Check that the URL redirects to the expected page, authentication is valid, and lazy images have loaded. A full-page option does not guarantee that JavaScript content finished rendering.

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

Unexpected layout differences

Compare viewport, scale, color scheme, locale, timezone, fonts, browser version and headless setting. Then inspect animations and dynamic regions. Do not mask the difference until you know it is intentional.

Element screenshot is cut off

A locator capture includes the element’s visible box. If the element contains its own scrollable area, only the currently scrolled content is captured. Scroll that container deliberately or capture the page region that contains the content you need.

Files are too large

Use WebP or JPEG with an appropriate quality value, capture the required region instead of the entire page, or use CSS scale. Keep PNG for lossless output and transparency requirements.

Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server when you want a URL-to-image or PDF request without maintaining Playwright browsers. Its clean-shot workflow accepts cookie and consent banners, then removes more than 60 known consent platforms, newsletter popups and chat widgets before capture; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads and cache hits cost nothing, and every response identifies the result with X-Page-Verdict and X-Billed headers.

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

One GET request returns PNG, JPEG, WebP or PDF. The API supports full-page captures with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets or a custom viewport, retina scale, PDF paper size/margins/landscape/page ranges, HTML/CSS to image, custom JavaScript and CSS, pre-capture clicks, hidden selectors, waits for selectors/delays/network idle, blocked ads/trackers/requests/resource types, custom headers/cookies/user agents/Authorization, timezone and geolocation, transparent backgrounds, resizing, selectable cache TTLs, signed public-image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API and an OpenAPI specification. Parameter names used by other screenshot APIs also work to ease migration.

Use the ScreenshotNeo documentation for authentication and options. The cURL equivalent is:

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

Python:

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)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

An MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients, allowing an AI agent to request captures directly. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Sign up free for ScreenshotNeo.

FAQ

Does headless mode change the screenshot?

It can. Playwright documents rendering differences involving headless mode and the host environment, so keep the mode consistent when creating visual baselines.

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

Can I get image bytes instead of a file?

Yes. Omit path and page.screenshot() returns a buffer that you can upload, hash or process in memory.

What is the difference between a locator screenshot and a clip?

A locator follows an element and scrolls it into view; a clip captures fixed coordinates. Locators are generally more resilient to layout movement.

Should I use screenshots on every test?

Use automatic failure artifacts when diagnosis is the goal. Add explicit screenshots or visual assertions to tests where the rendered appearance itself is the requirement.

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.

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