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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Use sample images as controlled test inputs, then compare the browser’s rendered output with a versioned screenshot baseline. A reliable test does not merely check that an image file exists. It exercises the states your interface supports—normal cards, different aspect ratios, responsive crops, galleries, and missing-image fallbacks—under a fixed browser and viewport. Playwright Test’s toHaveScreenshot() assertion can create the first reference image and compare later runs against it.

What you are actually testing

A fixture image is input to your page or component. A screenshot is evidence of the final rendered state after CSS, layout, fonts, JavaScript, lazy loading and browser rendering have done their work. Test both layers when useful:

  • Asset contract: the expected file exists, has the intended format and can be loaded.
  • Visual contract: the image appears in the right box, crop, position and responsive layout.

Keep the fixture set small and deterministic. Store files in your repository or another controlled location rather than relying on a changing third-party image URL. Useful fixtures include a landscape image for a card, a portrait image for an avatar, a wide image for a hero, and a deliberate missing-image case if the application implements a fallback. Do not add cases your product does not support merely to make the suite look comprehensive.

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.

Build deterministic sample-image fixtures

Give every state a stable path

Place files in a test fixture directory or your application’s static test assets, and reference them with predictable URLs such as /fixtures/landscape.jpg. Commit the binary files with the test code so a future run receives the same pixels. Avoid random generators, remote stock-photo URLs and timestamps embedded in filenames.

Exercise the states that can regress

  • Normal image with the expected aspect ratio.
  • Images that force object-fit: cover or contain behavior.
  • Small and large source dimensions, if your component handles both.
  • Lazy-loaded images after the relevant section enters the viewport.
  • A missing or deliberately broken URL when a fallback icon or placeholder is part of the design.
  • Dark mode, high-density (retina) rendering or responsive breakpoints when those are supported requirements.

Make the page state explicit

Arrange for the test to select a known product, article or component state. Seed the same data, disable random ordering and freeze feature flags. If the page uses a content API, intercept it with test data or run a local fixture endpoint. The screenshot should fail because the UI changed, not because production content changed overnight.

Capture and compare with Playwright Test

Install and create a test

toHaveScreenshot() is part of Playwright Test, not the lower-level browser API. A minimal test might look like this:

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

test('card renders the sample image', async ({ page }) => {
  await page.goto('/cards/sample');
  await expect(page.getByTestId('product-card')).toHaveScreenshot('card-landscape.png');
});

Use a locator when you want to test one component, or call await expect(page).toHaveScreenshot('cards-page.png') for the complete page. Keep the name meaningful; snapshot paths must remain inside Playwright’s configured snapshot directory.

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

Generate the first baseline

Run the test once in the browser and project configuration you intend to support. Playwright writes the reference screenshot on that first run. Review it immediately, then add the snapshot directory to version control. A baseline is an approved expectation, not an automatically trustworthy picture.

Run later comparisons

Subsequent runs capture the page and compare it with the stored file. Playwright takes captures until two consecutive screenshots match, helping transient rendering settle. You still need to wait for your own application state: for example, wait for a card’s image to be visible, a loading spinner to disappear, or a gallery to finish selecting its initial slide.

test('responsive gallery', async ({ page }) => {
  await page.setViewportSize({ width: 390, height: 844 });
  await page.goto('/gallery/sample');
  await page.getByRole('img', { name: 'Mountain lake' }).waitFor({ state: 'visible' });
  await expect(page).toHaveScreenshot('gallery-mobile.png', {
    animations: 'disabled',
    maxDiffPixels: 20
  });
});

Use a small maxDiffPixels allowance only when you understand the source of harmless variation. Do not hide the image region with a stylesheet: that would remove the thing being tested. Playwright also supports a custom stylesheet (for example, to conceal a clock or rotating advertisement) when the volatile element is unrelated to the sample image.

Control the rendering environment

Browser screenshots are not universal bitmaps. Rendering can vary with the host operating system, browser version and settings, hardware, power source, headless mode and other factors. Generate and compare a baseline in the same environment whenever possible.

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

Choose a project matrix deliberately

Define the browser, viewport and device scale factor in Playwright projects. If Chrome on Linux and WebKit on macOS are both supported release targets, give each its own snapshots rather than comparing both against one image. Snapshot names and path templates can include the project context, keeping the references separate.

Stabilize fonts and layout

  • Install the same fonts in local development and CI, or package the web fonts used by the test.
  • Wait for document.fonts.ready when late font swaps affect layout.
  • Disable animations and transitions for visual assertions.
  • Use a fixed viewport and device scale factor.
  • Wait for image decoding and lazy-loaded content before the assertion.
  • Remove clocks, random IDs, rotating carousels and network-dependent advertisements from the tested state.

Review differences instead of auto-accepting them

A diff is a review signal. Check the fixture bytes, intrinsic dimensions, CSS crop, container size, fonts, browser and intended design change. Update references only after that review, using:

npx playwright test --update-snapshots

Commit changed snapshot files like any other expectation change so reviewers can see exactly what was approved.

Test common sample-image failure modes

The image area is blank

Inspect the browser console and network log. A wrong path, a fixture not copied into the test build, a blocked request or a CSP rule can all produce a blank region. Assert the response status or the image’s completed state before taking the screenshot; otherwise the assertion may capture a loading placeholder.

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.

The crop changed unexpectedly

Compare the source aspect ratio, the CSS object-fit and object-position, and the component’s width at the test viewport. A fixture with a different focal point can reveal a legitimate design problem, so do not replace it with a more convenient image merely to make the diff disappear.

Only CI fails

Compare browser versions, operating systems, fonts, headless settings, device scale factors and power conditions. If the team intentionally supports multiple renderers, create project-specific baselines. If not, run visual tests in a pinned container or equivalent controlled environment.

Every run differs by a few pixels

Look for animation, font fallback, fractional layout dimensions, caret blinking, timestamps and image decoding races. Disable or wait for those sources first. Increase a pixel tolerance only after confirming the variation is irrelevant to the behavior under test.

A missing-image test never reaches the fallback

Use a deterministic invalid path or intercept the request, then wait for the component’s error state (often an error event or a visible fallback locator). Capture that state separately from the successful-image baseline.

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

Local Playwright or hosted review?

Playwright’s local expectations are a practical starting point for a small suite. Baselines and diffs live with the code, CI runs the same assertions, and pull-request reviewers can inspect changed image files. A hosted workflow can be useful when many contributors need a shared review queue, archived page captures or broader browser and viewport coverage.

Consideration Playwright local snapshots Chromatic Playwright integration
Storage Reference images in the repository’s snapshot directory. Chromatic documents uploading page archives and storing captures in its service.
Review Review image diffs in code review and approve by updating snapshots. Interactive cloud inspection and accept/reject workflow documented by Chromatic.
Coverage You configure browsers, projects and viewports in Playwright. Chromatic documents viewport and cross-browser coverage options.
CI Run npx playwright test in your pipeline. Use the documented Playwright integration in CI.
Cost No price comparison is established here. No price comparison is established here.

Choose based on where your team wants baselines stored, how reviewers investigate a change, the browser matrix you must cover and who is responsible for maintaining approved references. Neither approach makes an intentional design decision automatically correct.

When a design reference is involved

Visual regression normally compares current browser output with an approved prior output. Design acceptance compares the output with a design reference such as a Figma frame. They answer different questions: “Did the implementation change?” versus “Does the implementation match the intended design?” Keep the sample image and viewport identical in both comparisons, and record whether a discrepancy comes from the design, the fixture or the implementation.

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

Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server when you need a rendered page image without maintaining browser automation. It accepts consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets before capture; failed loads, bot checks or CAPTCHAs, blank pages and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP tools—take_screenshot, get_page_info and capture_pdf—work with Claude, Cursor and other MCP clients.

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

Use the API documentation at https://screenshotneo.com/docs/ for authentication and options. A one-call capture of a page containing your fixture can be made with cURL:

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}`);

ScreenshotNeo supports full-page and element captures, lazy-image loading, custom CSS and JavaScript, waits, request blocking, cookies and headers, device and viewport settings, dark mode, PDFs, resizing, caching, signed links, asynchronous webhooks and bulk capture of up to 100 URLs per call. The Free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account to try it.

Practical release checklist

  • Fixtures are local, deterministic and committed.
  • Each screenshot names the state, viewport and browser project.
  • The test waits for fonts, images and application state.
  • Baselines were generated in the intended rendering environment.
  • Snapshot changes are reviewed rather than blindly updated.
  • Failure logs identify whether the problem is an asset, layout, environment or intentional design change.

Frequently Asked Questions

What file format should a visual baseline use?

Playwright uses PNG by default and also documents WebP snapshots when the filename ends in .webp. Choose one format consistently for a given project.

Should I compare the whole page or only the image component?

Use a component locator when the question is image rendering in isolation; use a page screenshot when surrounding layout, responsive behavior or overlays are part of the requirement.

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

How often should snapshots be regenerated?

Regenerate only after reviewing a confirmed intentional change. Keep the updated files in version control so the approval is traceable.

Can hosted review replace local visual assertions?

It can provide a shared capture and approval workflow, but you still need deterministic fixtures and a controlled browser state; hosting does not remove rendering variability.

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.