How to Capture a Webpage Screenshot with Playwright in JavaScript
Capture viewport, full-page, or element screenshots with Playwright in JavaScript. Learn the options, reliable patterns, and common fixes.
Use Playwright’s page.screenshot() method after navigating to the page. It returns a Buffer; pass a file path to save the image. Set fullPage: true for the whole scrollable document, or call screenshot() on a locator to capture one element.
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: 'screenshot.png' });
} finally {
await browser.close();
}
})();
This guide covers a runnable setup, viewport and full-page captures, element and clipped screenshots, output formats, repeatability, visual tests, troubleshooting, performance, and when to use a screenshot API instead.
1. Install Playwright and capture your first screenshot
In a new Node.js project, install Playwright and its browser binaries:
npm init -y
npm install playwright
npx playwright install chromium
Save this as screenshot.js and run node screenshot.js. Playwright launches Chromium, creates a page, navigates to the target, saves a PNG, and closes the browser even if navigation or capture throws an error. The official example uses WebKit; Chromium or Firefox can be used in the same pattern.
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: 'screenshot.png' });
} finally {
await browser.close();
}
})();
For an ECMAScript module, use import { chromium } from 'playwright'; instead of require, and run the file in an ESM-enabled project. See the Playwright page screenshot API and screenshot guide.
2. Choose the capture scope
Visible viewport
By default, page.screenshot() captures the current viewport. Set its dimensions when creating the page so the output is predictable:
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
await page.goto('https://example.com');
await page.screenshot({ path: 'viewport.png' });
Full page
Set fullPage: true to capture the full scrollable document rather than only the visible viewport:
await page.screenshot({ path: 'full-page.png', fullPage: true });
Full-page output can be much taller and larger than a viewport image. If a page loads content only after scrolling, wait for that content to appear or scroll through the relevant sections before capturing; full-page mode describes the capture extent, while page behavior determines what has loaded.
One element
Use a locator screenshot when you need a component such as a header, chart, or product card. Playwright scrolls the element into view as needed:
await page.locator('.header').screenshot({ path: 'header.png' });
Prefer a stable selector, such as a test ID, when the page is under your control. If multiple elements match, refine the locator so the intended target is unambiguous.
Rectangular region
Use clip to capture a rectangle in page viewport coordinates:
await page.screenshot({
path: 'region.png',
clip: { x: 80, y: 120, width: 640, height: 360 }
});
The clip must describe a valid, positive-size rectangle within the capture area. Use an element locator instead when the region should follow an element’s position and dimensions.
3. Select format, scale, and quality
| Option | Behavior | When to use it |
|---|---|---|
type |
png, jpeg, or webp; defaults to PNG. |
Choose the format required by the next tool or consumer. |
path |
Writes a file; its extension determines the image type when supplied. | Use for artifacts or files consumed by another process. |
quality |
Applies to JPEG and WebP, not PNG. JPEG defaults to 80; WebP defaults to 100, described by Playwright as lossless. | Set for lossy formats when balancing size and fidelity. |
scale |
css produces one pixel per CSS pixel; device uses device pixels. |
Use CSS scale for compact, consistent CSS-sized captures; device scale for denser output. |
For example, capture a compressed WebP file at CSS-pixel scale:
await page.screenshot({ path: 'page.webp', type: 'webp', quality: 82, scale: 'css' });
When path is omitted, the method returns image bytes as a Buffer instead of writing a file. Relative paths resolve from the process’s current working directory. If both a path extension and an explicit type are supplied, keep them consistent so the filename accurately describes the data.
4. Make captures repeatable
Dynamic pages can produce different images on each run. Disable animations and hide the text caret for a calmer capture; use temporary CSS or masks to control known moving or sensitive regions:
await page.screenshot({
path: 'stable.png',
animations: 'disabled',
caret: 'hide',
style: '.timestamp { visibility: hidden !important; }',
mask: [page.locator('.avatar')]
});
Playwright’s screenshot options include temporary style injection and mask locators. Use masking only when replacing a region is acceptable for the purpose of the capture; it changes what the image shows. If a page contains continuously changing data, arrange deterministic test data or wait for the application to reach a known state before capture.
Wait for the content you need
page.goto() supports navigation wait conditions. A screenshot may otherwise happen before application content, fonts, or images are ready. Wait for a meaningful selector when possible:
await page.goto('https://example.com', { waitUntil: 'load' });
await page.locator('main').waitFor({ state: 'visible' });
await page.screenshot({ path: 'ready.png' });
Choose readiness conditions that match the page. A visible application landmark is often more useful than assuming every background request has completed. For lazy-loaded material, scroll the relevant content into view and wait for it before capturing.
5. Use screenshots in a visual regression test
With Playwright Test, toHaveScreenshot() creates a reference image on its first run and compares subsequent captures against that baseline. It waits until two consecutive screenshots match before comparing:
import { test, expect } from '@playwright/test';
test('home page visual baseline', async ({ page }) => {
await page.goto('https://example.com');
await expect(page).toHaveScreenshot('home.png');
});
Run visual comparisons in a consistent environment. Operating system, browser version, settings, hardware, power source, and headless mode can all affect rendering. Review baseline changes deliberately and keep the environment used to create and compare snapshots aligned. Read the visual comparisons guide.
6. Troubleshoot common problems
| Symptom | Likely cause | Fix |
|---|---|---|
| Browser executable is missing | The Playwright package is installed but its browser binary is not. | Install the browser for the engine you use, for example npx playwright install chromium. |
| Screenshot is blank or incomplete | Capture ran before the app rendered, or content requires scrolling or interaction. | Wait for a visible application selector; trigger the needed interaction or scroll and wait for lazy content. |
| Navigation times out | The page is slow, unreachable, or waiting for a lifecycle event that does not occur promptly. | Check the URL and network access, then choose an appropriate navigation condition and wait for the content you need. |
| Element screenshot fails | The locator matches no visible element or the element is not ready. | Verify the selector and wait for the locator to become visible before calling its screenshot method. |
| Unexpected image format | File extension and requested type do not match, or the extension is missing. | Use a matching path suffix such as .webp, or explicitly set type when returning bytes. |
| Visual test differs across machines | Rendering environment or dynamic content differs. | Align operating system, browser, settings, and headless mode; stabilize data and animations before updating a baseline. |
| Output is unexpectedly huge | Full-page scope or device-pixel scale creates many pixels. | Capture the viewport or element, use scale: 'css', or choose an appropriate compressed format and quality. |
7. Performance, reliability, and cost
Browser startup and page rendering are usually the expensive parts of a one-off capture. Close the browser in a finally block, reuse a launched browser for multiple pages in a controlled batch, and avoid capturing full documents when only a viewport or component is needed. Large full-page images consume more memory and take longer to encode or transfer.
For reliable automation, use explicit readiness checks, bounded timeouts appropriate to your workload, stable viewport dimensions, and deterministic page data. A screenshot only records the state the browser can render; it does not establish that the page is correct. In visual regression workflows, baseline review and a consistent capture environment are part of the process.
Playwright itself is an open-source browser automation library, but running captures has infrastructure costs: compute, browser binaries, storage, and any network or proxy resources your setup requires. Actual cost depends on where and how often you run it; the cited Playwright documentation does not publish a universal screenshot price or performance benchmark.
8. Or skip the browser setup
If you need screenshots without managing browser installation and capture code, ScreenshotNeo is a website screenshot API and MCP server. Its API accepts one GET request and returns an image or PDF. See the ScreenshotNeo API documentation.
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}`);
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up free for 1,000 screenshots a month, no card required.
9. Frequently asked questions
Does Playwright return screenshot bytes?
Yes. page.screenshot() returns a Buffer; supplying path also saves the image to disk.
Can I capture just the currently visible screen?
Yes. Viewport capture is the default, so omit fullPage or leave it false.
Can I screenshot an element instead of the whole page?
Yes. Call screenshot() on a locator for the element capture.
Which format should I choose?
PNG is the default. Choose JPEG or WebP when those formats fit the consuming system and you want to use their quality setting; select the output type to suit the image’s purpose.


