PLAYWRIGHT TUTORIAL

Create stable Playwright screenshots and useful visual tests

Use page and locator screenshots for local browser automation, then control viewport, timing, animation, fonts, volatile regions, baseline ownership, and CI environment before trusting a visual comparison. Choose a screenshot API when you need a focused remote output instead of a programmable browser session.

page.screenshot
Viewport or full page
locator.screenshot
Element capture
toHaveScreenshot
Playwright Test comparison
Install:npm install -D @playwright/test && npx playwright install
Auth:CI=true npx playwright test
Get started for free
Visual test workflow
Abstract cross-browser Playwright test harness producing viewport, full-page, and element screenshots
Good fits
End-to-end flows that already use Playwright
Component and element-level visual regression
Authenticated browser states and interaction-heavy pages
Cross-browser screenshot checks in controlled CI

A screenshot is only as stable as its browser state

Playwright makes image capture simple. Reliable visual testing still depends on deterministic content, browser version, operating system, fonts, viewport, animation, and approved baseline policy.

1

Capture the right scope

Use the default viewport screenshot, fullPage for the scrollable document, or locator.screenshot for one component.

2

Control unstable pixels

Disable animations, mask volatile regions, seed data, and use the style option for test-only stabilization when appropriate.

3

Keep environments comparable

Generate and compare baselines with the same browser build, operating system, fonts, device scale, and project settings.

4

Review the diff

A threshold is a triage policy, not proof that a changed page is wrong. Preserve expected, actual, and diff artifacts.

Quick start

Capture and compare a stable page

1

Install Playwright Test and its managed browser binaries

2

Set an explicit viewport and navigate to a deterministic test state

3

Capture the page or a locator with animations disabled

4

Commit an approved baseline and review diff artifacts in the same CI environment

Playwright screenshot examples

Playwright

Viewport, full-page, and element captures

The screenshot methods can write to a path or return image bytes when path is omitted.

import { test, expect } from "@playwright/test";

test.use({ viewport: { width: 1440, height: 900 } });

test("capture stable page states", async ({ page }) => {
  await page.goto("https://example.com", { waitUntil: "load" });

  await page.screenshot({ path: "artifacts/viewport.png" });

  await page.screenshot({
    path: "artifacts/full-page.png",
    fullPage: true,
    animations: "disabled",
    mask: [page.locator("[data-volatile]")],
  });

  await page.locator("main").screenshot({
    path: "artifacts/main.png",
    animations: "disabled",
  });

  await expect(page).toHaveScreenshot("homepage.png", {
    animations: "disabled",
    maxDiffPixels: 100,
  });
});

Install Playwright for screenshot testing

For a test suite, install @playwright/test and the browser binaries it manages. Keep the package and downloaded browsers aligned through the normal Playwright install command. Pinning the project dependency makes a browser upgrade an explicit reviewed change rather than an accidental baseline rewrite.

Define viewport, color scheme, locale, timezone, and other relevant context options in the Playwright project configuration. A screenshot without those inputs is hard to reproduce. If a page behaves differently by region or time, make that state explicit or replace the changing service with a deterministic test fixture.

Choose viewport, full page, or locator

page.screenshot() captures the current viewport by default. This is the right scope when the assertion concerns above-the-fold layout or a fixed application screen. Set path to write the image; omit it when code needs the returned bytes.

Set fullPage: true when the complete scrollable document is the subject. Long captures are sensitive to lazy loading, sticky elements, infinite lists, and content that changes during scroll. A full-page image is not automatically a better test: it creates a larger diff surface and can make an unrelated footer change fail a header assertion.

Use locator.screenshot() for a component, card, chart, dialog, or other bounded region. Element screenshots reduce noise and make ownership clearer. The locator must still identify the intended element reliably, and the element's surrounding state may influence its layout.

Wait for the state you mean to test

Navigation completion does not prove that every page-specific task has finished. A single-page application may hydrate after load; a chart may draw after data arrives; a web font may swap; a skeleton may disappear only after a business condition is met.

Wait for an application-level signal such as a meaningful locator, a response, or a controlled data state. Avoid a large arbitrary timeout as the primary strategy. It makes every run slower and can still be too short under load.

For fonts, wait until the document's font set reports readiness when typography is part of the assertion. For lazy media, drive the page through the intended state or test the component in a fixture that loads deterministically. Do not silently scroll an infinite feed and call the result stable.

Stabilize animations and volatile regions

Playwright's screenshot options can disable animations. That handles CSS animations and transitions according to the documented behavior, but it does not freeze every source of change. Video frames, canvas rendering, current timestamps, randomized recommendations, cursor blinking, ads, and live counters need separate treatment.

Use mask for a narrow region that is expected to change and is not part of the assertion. A mask should be a documented exception. If most of the page is masked, the test no longer protects meaningful behavior.

The style screenshot option can inject test-only CSS that pierces shadow DOM and applies during capture. It is useful for hiding a known cursor or pausing an application-owned effect. Keep that CSS next to the test and explain why each selector is excluded.

Build a trustworthy baseline workflow

Playwright Test's expect(page).toHaveScreenshot() compares the current capture with a stored snapshot. The first accepted run creates the reference. Later runs produce expected, actual, and diff artifacts when the result exceeds the comparison policy.

Generate baselines in the environment used for comparison. Playwright's documentation warns that screenshots vary with operating system, browser version, hardware, power source, headless mode, and other environmental factors. A container or controlled CI image can reduce that variance, but fonts and application data still need management.

Review baseline changes like code. A mass update command can make the suite green by approving a regression. Separate intended design updates from unrelated test maintenance, inspect the rendered diff, and let the responsible reviewer accept the new state.

Set thresholds without hiding regressions

Pixel-perfect comparison can be appropriate for a controlled component. A small tolerance can help with known antialiasing variance, but every tolerance weakens the assertion. Prefer making the environment stable before raising maxDiffPixels, maxDiffPixelRatio, or a per-pixel threshold.

A threshold answers whether a test should demand review. It does not decide whether the product is correct. Preserve the diff image and relevant request settings so a person can interpret the changed region.

Make screenshots useful in CI

Upload actual and diff artifacts on failure. Include the test name, browser project, commit, viewport, and retry number. Retrying can distinguish intermittent state from a persistent change, but a flaky first attempt should remain observable rather than disappearing from reports.

Avoid parallel tests that mutate the same account or baseline state. Give each test deterministic data and isolate user sessions. Use a web server readiness check rather than guessing how long the application needs to start.

Keep visual suites focused. A small set of representative routes and components can provide more signal than thousands of broad snapshots that reviewers habitually approve.

Playwright versus a screenshot API

Playwright is the right tool when the test must sign in, click, type, intercept requests, exercise several browsers, or inspect application state before capture. Your code owns the browser workflow and can assert behavior alongside pixels.

Snapshot Site is a different abstraction. The application submits a URL and output settings to a hosted website screenshot API, or sends two sources to the Visual Diff API. It does not replace arbitrary browser steps. It can remove browser fleet and image-comparison work from output-oriented services, scheduled page checks, and integrations that need a stable API result.

Use both when the boundaries fit: Playwright can validate authenticated interaction paths in CI, while an external SEO monitoring or ecommerce monitoring job checks public production pages on a schedule.

Production checklist

  1. Pin Playwright and install its matching browsers.
  2. Fix viewport, browser project, locale, timezone, color scheme, and relevant context.
  3. Seed deterministic data and wait for application-level readiness.
  4. Capture only the scope owned by the assertion.
  5. Disable animations and mask only documented volatile regions.
  6. Generate baselines in the comparison environment.
  7. Start with strict comparison and add tolerance only for measured noise.
  8. Upload expected, actual, and diff artifacts on failure.
  9. Review baseline updates rather than approving them mechanically.
  10. Revisit flaky screenshots as product or test defects, not harmless background noise.

Playwright screenshot FAQ

How do I take a full-page screenshot in Playwright?

Call page.screenshot with fullPage: true. Playwright captures the scrollable page rather than only the current viewport. Test long pages with lazy content and fixed elements.

How do I screenshot one element?

Create a locator for the intended element and call locator.screenshot. Prefer a stable role, label, test ID, or structural locator over a fragile generated class.

Can Playwright return screenshot bytes instead of writing a file?

Yes. If path is omitted, screenshot methods return the image buffer so the test or application can upload, compare, or transform it in memory.

How do I disable animations for screenshots?

Use animations: disabled in screenshot or toHaveScreenshot options. Also stabilize timers, rotating content, video, live data, and other sources that are not CSS animations.

What does expect(page).toHaveScreenshot do?

Playwright Test captures the page and compares it with an approved snapshot, producing artifacts when pixels differ beyond the configured policy. The initial accepted run creates the baseline.

Why do screenshots differ between a laptop and CI?

Browser version, operating system, font files, GPU behavior, device scale, color rendering, and test data can change pixels. Create and compare baselines in a consistent environment.

Should I mask dynamic content?

Mask regions only when their content is intentionally outside the assertion. Broad masks can hide real regressions, so keep them narrow and documented.

When should I use Snapshot Site instead of Playwright?

Use Playwright for interactions, authentication, browser projects, and tests. Use Snapshot Site when an application needs a focused hosted capture, compare, or analyze result without owning browser automation.

Choose one representative visual test

Stabilize its state, approve the baseline in the target CI environment, and inspect the first intentional diff before expanding the suite.