Playwright: A Guide to Browser Automation and Screenshots
Install Playwright’s matching browsers, automate pages, capture screenshots, compare visual baselines, and diagnose failures with traces.
Playwright automates Chromium, Firefox, and WebKit, and its Page API captures viewport, full-page, or element screenshots. Install the browser binaries that match your Playwright package version, then navigate a page and call page.screenshot(). For repeatable visual checks, use Playwright Test’s toHaveScreenshot() and keep the baseline and comparison environments aligned. For CI failures, record a trace on retry and inspect it in Trace Viewer.
Playwright is both an automation library and, through Playwright Test, a test runner. The package version matters: each Playwright version expects specific browser binaries. Installing or upgrading the package does not mean your system’s everyday Chrome, Firefox, or Safari is automatically the matching browser. Use the Playwright CLI to install the engines for the installed version. See the official browser installation guide.
1. Install Playwright and its browsers
For a JavaScript project using Playwright Test:
npm init playwright@latest
The setup command creates a starter test project and can install browsers. In an existing project, add Playwright Test and install the browser binaries through its CLI:
npm install --save-dev @playwright/test
npx playwright install
To install one engine only, specify it explicitly:
npx playwright install chromium
# or
npx playwright install firefox
# or
npx playwright install webkit
Playwright also documents branded Chrome and Edge channels, device emulation, operating-system support, and system dependencies. Choose an engine and mode that reflect the workflow you need: Chromium, Firefox, and WebKit provide engine coverage; branded browsers can be useful when you specifically need Chrome or Edge behavior. The downloaded Playwright browser builds are tied to the installed Playwright release.
After upgrading Playwright, install browsers again if the expected binaries are missing or the CLI reports a version mismatch:
npm install --save-dev @playwright/test@latest
npx playwright install
In CI, the runner may also need OS-level browser dependencies. Follow the official CI setup guidance for the operating system and environment you use; do not assume that downloading browser binaries alone supplies every system library.
2. Launch a browser and capture a screenshot
This runnable Node.js script opens a page in Chromium, waits for navigation, saves the visible viewport, and closes the browser even if capture fails. Save it as screenshot.mjs, install the package and Chromium as above, then run node screenshot.mjs.
import { chromium } from 'playwright';
const browser = await chromium.launch({ headless: true });
try {
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.screenshot({ path: 'screenshot.png' });
} finally {
await browser.close();
}
If your project installed only @playwright/test, import chromium from @playwright/test instead, or add the playwright package. The basic screenshot call is await page.screenshot({ path: 'screenshot.png' }). The exact available options can change across Playwright releases; consult the Page screenshot API for the version in your project.
Viewport, full-page, element, and buffer capture
| Capture type | Use | Example |
|---|---|---|
| Viewport | The currently visible browser area | await page.screenshot({ path: 'viewport.png' }) |
| Full page | The scrollable document, including content below the fold | await page.screenshot({ path: 'full.png', fullPage: true }) |
| Element | A specific matched element, such as a chart or card | await page.locator('.report').screenshot({ path: 'report.png' }) |
| Buffer | Image bytes for upload, comparison, or processing without first writing a file | const bytes = await page.screenshot() |
A locator screenshot waits for the target locator to resolve and captures that element. If the selector matches multiple elements, make it unique or choose the intended match explicitly. A full-page image can be much taller than the viewport and use more memory; for very long pages, consider whether a viewport or selected-element capture answers the task better. Full-page capture is not the same as proving that every lazy-loaded asset has finished loading: scroll or otherwise trigger the page’s own lazy-loading behavior when those assets matter, then wait for the relevant content before capturing.
Control what the page shows before capture
Automation makes it possible to establish a deliberate capture state: set the viewport before navigation, wait for a selector that signals readiness, dismiss a dialog if the test requires it, or use a locator screenshot to focus on a component. Avoid treating an arbitrary delay as a guarantee that a page is ready. A fixed timeout can be too short on a slow run and waste time on a fast one. Prefer an observable condition, such as a visible heading or completed application state. Browser automation can also capture pages that display cookie banners, newsletter popups, or chat widgets; if those are not part of what you want to evaluate, handle them explicitly in your script or hide them in a controlled test setup.
3. Use Playwright Test for repeatable browser automation
Playwright Test provides fixtures for browser, context, and page, so a test can focus on the page behavior. Save this as tests/homepage.spec.js in a project created with Playwright Test:
import { test, expect } from '@playwright/test';
test('homepage renders and can be captured', async ({ page }) => {
await page.setViewportSize({ width: 1440, height: 900 });
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await expect(page.getByRole('heading', { name: 'Example Domain' })).toBeVisible();
await page.screenshot({ path: 'artifacts/homepage.png', fullPage: true });
});
Create the output directory before running if your setup does not create it:
mkdir -p artifacts
npx playwright test
The test runner can run tests across configured projects and browser engines. Configure Chromium, Firefox, and WebKit when cross-engine behavior matters, and keep the same project and browser versions for image baselines and comparisons. Use Playwright Test configuration to set projects, retries, reporters, and other runner behavior.
4. Compare visual baselines with toHaveScreenshot
For visual regression checks, Playwright Test’s toHaveScreenshot() saves a reference image on its first run, then compares later runs against it. The first capture is therefore baseline creation, not an independent pass/fail comparison. Review and commit the generated baseline intentionally; when a visual change is expected, regenerate and inspect the snapshot changes.
import { test, expect } from '@playwright/test';
test('homepage visual baseline', async ({ page }) => {
await page.setViewportSize({ width: 1440, height: 900 });
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await expect(page).toHaveScreenshot('homepage.png', { fullPage: true });
});
Run the test to create or compare snapshots:
npx playwright test
Visual output can vary with the host operating system, browser version, settings, hardware, power source, headless mode, fonts, and other environmental factors. Create and compare baselines in the same controlled environment, including the same Playwright version and browser binaries. The official visual comparisons guide explains snapshot behavior and configuration. Avoid accepting every changed image automatically: inspect whether the change is intended before updating a baseline.
5. Diagnose failures with traces
When a test fails in CI, a trace gives you a timeline of actions and page state to inspect. Playwright’s CI guidance commonly records a trace on the first retry, which provides failure evidence without collecting a trace for every successful test. Configure this in playwright.config.js:
import { defineConfig } from '@playwright/test';
export default defineConfig({
retries: 1,
use: {
trace: 'on-first-retry',
},
});
Open a saved trace with the CLI:
npx playwright show-trace path/to/trace.zip
The Trace Viewer can show action history, DOM snapshots, source locations, console and network activity, metadata, and attachments. These details help distinguish a selector that never appeared from a navigation failure, a request error, or an unexpected page state. See the official Trace Viewer guide and viewer introduction.
There is an important API boundary: the low-level context.tracing API captures browser operations and network activity, but it does not capture Playwright Test assertions. For test failure traces that include the test-runner context, configure tracing through Playwright Test as above. See the tracing API documentation.
6. Choose an engine and capture workflow
| Need | Approach | Consideration |
|---|---|---|
| Automate a standard browser engine | Install and launch Chromium, Firefox, or WebKit | Install binaries matching the package version; engine differences may affect rendering |
| Check Chrome or Edge specifically | Use the documented branded browser channel | Follow current Playwright documentation for channel availability and setup |
| Capture an ordinary page image | page.screenshot() |
Captures the viewport unless configured otherwise |
| Capture a long document | page.screenshot({ fullPage: true }) |
Large pages can produce large images and take additional memory |
| Capture one component | locator.screenshot() |
Use a stable, unique locator and ensure the component is in its intended state |
| Check for unintended visual changes | Playwright Test with toHaveScreenshot() |
Baseline quality depends on a consistent environment and reviewed updates |
| Investigate intermittent CI failures | Playwright Test tracing on retry | Tracing on every test can add performance and storage overhead |
7. Troubleshoot common problems
| Symptom | Likely cause | Fix |
|---|---|---|
| “Executable doesn’t exist” or browser launch fails | The browser binary expected by the installed package is not installed | Run npx playwright install, or install the specific engine. In CI, install required system dependencies using the official CI instructions. |
| Browser version mismatch after an upgrade | The package changed but its expected browser build was not downloaded | Rerun npx playwright install after changing Playwright versions. |
| Navigation times out | The site is slow, waits on long-lived network activity, or the chosen readiness condition is unsuitable | Choose a readiness condition that matches the page, wait for a specific selector or state, and inspect the trace’s network activity. Do not blindly increase timeouts without checking the cause. |
| Screenshot is blank or incomplete | The page has not rendered its content yet, an app error interrupted rendering, or lazy content was never triggered | Wait for an observable page condition, inspect console and network activity, and trigger lazy loading if needed before capture. |
| Element screenshot fails or captures the wrong area | The locator is missing, ambiguous, hidden, or not in the expected state | Use a unique locator, assert visibility, and inspect the DOM snapshot in a trace. |
| Visual test fails on CI but passes locally | Rendering environments differ in OS, browser binaries, fonts, headless mode, hardware, or settings | Run baseline generation and comparison in the same environment and pin the Playwright project and browser version consistently. |
| Trace does not show an assertion | Tracing was started with low-level context.tracing, which does not capture test assertions |
Use Playwright Test’s tracing configuration, such as trace: 'on-first-retry'. |
| Trace files or test runs become costly | Recording traces for every test increases work and artifacts | Record on retry or only for selected diagnostics, and retain only the artifacts needed to investigate failures. |
8. Performance, reliability, and cost
Playwright runs a browser process and its supporting resources, so capture cost includes browser startup, navigation, page rendering, image encoding, and any waiting or diagnostic recording. Reuse a browser process for multiple pages where the workflow permits, while using separate contexts when tests need isolated browser state. Keep screenshots focused: full-page capture and very large images need more memory and produce larger artifacts than viewport or element captures. Traces are useful evidence, but enabling them on every run adds overhead; retry-based recording is a practical CI default documented by Playwright.
For reliability, install the matching browser builds in each environment, wait for page conditions that represent readiness, use stable selectors, and keep visual baselines tied to a controlled environment. Prefer traces to guesswork when failures are intermittent. Playwright itself does not charge per screenshot; infrastructure, CI execution time, browser resources, and artifact storage are the operational costs to plan for. The research sources provide no benchmark or fixed runtime estimate, so measure your own pages and CI environment.
9. Or skip the browser setup
If you need a screenshot endpoint rather than managing browser installation, ScreenshotNeo is a website screenshot API and MCP server. One GET request takes a URL and returns PNG, JPEG, WebP, or PDF. Its API documentation covers the parameters, and the parameter names used by other screenshot APIs also work.
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}`);
- Cookie banners are accepted and removed before capture; known consent platforms, newsletter popups, and chat widgets are removed, and each step can be turned off.
- Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers report the page verdict and whether the request was billed.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for Claude, Cursor, and other MCP clients. - The API also supports full-page and element capture, device presets and custom viewports, PDF settings, custom CSS and JavaScript, waits, request blocking, headers and cookies, caching, signed links, async jobs, bulk capture up to 100 URLs per call, and a usage API.
- The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is on every plan.
Sign up free for 1,000 screenshots a month, with no card required.
10. Frequently asked questions
How do I take a screenshot?
Navigate a Playwright page, then call await page.screenshot({ path: 'screenshot.png' }). Use fullPage: true for the scrollable document or a locator’s screenshot() method for an element.
How do I take a full page screenshot?
Call await page.screenshot({ path: 'full.png', fullPage: true }). If the page loads content as it scrolls, trigger that loading behavior and wait for the content before capture.
How do I install browsers?
Run npx playwright install for the browser builds expected by the installed Playwright version, or specify chromium, firefox, or webkit.
How do I debug tests on CI?
Configure Playwright Test tracing, commonly with trace: 'on-first-retry', then open the trace archive in Trace Viewer to inspect actions, DOM snapshots, console output, and network activity.
Does the low-level tracing API record test assertions?
No. The low-level context tracing API records browser operations and network activity. Configure tracing through Playwright Test for test-runner failure traces.


