ScreenshotNeo

BlogHow-to

How to Ignore Changes in Visual Regression Testing

Make visual tests stable by controlling screenshot inputs and ignoring only the pixels that are truly unpredictable. Compare Playwright, Applitools, Chromatic, and Percy approaches.

By the ScreenshotNeo team4 October 202610 min read

To ignore a visual change safely, first make the screenshot reproducible, then mask or normalize the smallest unpredictable region that cannot be controlled. Keep the rest of the page under comparison, and add a functional assertion for any changing value whose meaning matters. Avoid raising a global diff threshold or excluding a whole component just to make a failing test pass.

For example, if a timestamp changes on every run, mask its locator in the screenshot assertion. If the timestamp itself is important, assert its value separately. A mask removes visual scrutiny from that region; it does not prove the content is correct.

1. Stabilize the screenshot before ignoring anything

A screenshot can change because the application changed, or because the capture environment changed. Playwright notes that rendering may vary with the host operating system, browser version, settings, hardware, power source, and headless mode. Keep the baseline and comparison environment consistent before adjusting the comparison rules.

  • Use the same browser engine and version when creating and checking baselines.
  • Run baseline generation and comparison on the same operating system and, where possible, the same CI image.
  • Control test data, clocks, random values, and third-party responses when practical.
  • Wait for the page state your test intends to compare. Avoid taking a screenshot while content is still loading.
  • Stabilize animations and transitions. Playwright screenshot assertions disable animations by default; finite animations are fast-forwarded and infinite animations are canceled for capture, then resumed.

Playwright’s visual comparisons guide explains environment sensitivity and screenshot testing. Its PageAssertions API documents screenshot assertion options and animation behavior.

2. Choose the narrowest suppression that solves the problem

Use this order of preference: control the source of variation, normalize it at capture time, then mask a specific region if the pixels still cannot be made stable. Choose a broader comparison mode only when you intend to stop checking content differences.

Method What it suppresses What remains checked Main risk
Control or stub the data The source of the changing UI, when the test can provide stable data The whole rendered page A stub can drift from production behavior if it does not represent a realistic state.
Capture-time CSS or style override Selected content or styling during the screenshot The rest of the page after the override Hidden or altered content is not visually checked.
Locator mask or ignore region A selected element or rectangle Pixels outside the masked region The masked content is not visually validated; bounding-box semantics vary by tool.
Disable a story snapshot The entire story or test snapshot No screenshot comparison for that target A real regression in that story will not be caught by the snapshot.
Layout-oriented comparison Some content differences according to the tool’s matching algorithm Layout or structure as defined by that tool Text or image changes you care about may be tolerated.
Increase a pixel threshold Small differences across the comparison Changes outside the configured tolerance A real bug affecting a small area can be accepted as noise.

These tools’ options do not have identical semantics. Read the documentation for the integration and version you use, and inspect the actual diff after changing suppression rules.

3. Ignore a dynamic region in Playwright

Playwright’s locator mask option covers the locator’s bounding box with a colored overlay in the screenshot comparison. The rest of the image is still compared. Confirm that the installed Playwright version supports the options you use.

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

test('dashboard matches its visual baseline', async ({ page }) => {
  await page.goto('http://localhost:3000/dashboard');
  await page.getByRole('heading', { name: 'Dashboard' }).waitFor();

  // Check the changing value separately if its meaning matters.
  await expect(page.locator('[data-testid="account-status"]')).toHaveText('Active');

  // Ignore only the unpredictable timestamp pixels.
  await expect(page).toHaveScreenshot('dashboard.png', {
    fullPage: true,
    mask: [page.locator('[data-testid="updated-at"]')],
  });
});

The example assumes a Playwright Test project and an application available at the local URL. Replace the URL and selectors with your application’s values. Prefer a stable test ID or other selector that identifies only the volatile element. If the locator matches multiple elements, decide whether all of them should be masked or narrow the locator.

For a test that should compare a viewport screenshot rather than a full page, remove fullPage: true. For multiple volatile elements, add each locator to the mask array. Playwright also supports screenshot options such as animations, caret, scale, and comparison thresholds; consult the API reference for their exact behavior and defaults.

Normalize content with a screenshot stylesheet

When the page has several instances of a volatile value, a capture-time stylesheet can hide or normalize them. Playwright’s visual comparisons guide documents stylePath for applying styles during screenshot capture. Keep this file narrowly scoped and review changes to it with the test.

/* tests/visual-stability.css */
[data-testid="updated-at"] {
  visibility: hidden !important;
}

/* Disable transitions for elements that still animate during capture. */
.visual-test-no-transition,
.visual-test-no-transition * {
  transition: none !important;
}
import { test, expect } from '@playwright/test';

test('dashboard is visually stable', async ({ page }) => {
  await page.goto('http://localhost:3000/dashboard');
  await expect(page).toHaveScreenshot('dashboard.png', {
    fullPage: true,
    stylePath: 'tests/visual-stability.css',
  });
});

Use visibility: hidden when you want the element to keep its layout space but not show its changing pixels. Use display: none only when removing the element and its layout space is part of the intended capture. If layout changes around the element matter, hiding it can create a misleadingly stable screenshot; masking the small value is often safer.

4. Apply the equivalent feature in other visual testing tools

Tool names and integration behavior differ. The following are documented mechanisms; check the current documentation for your installed integration and verify the effect on the diff.

  • Applitools: its Playwright integration accepts ignoreRegions, including a locator. Its help covers ignorable regions, dynamic content, and dynamically positioned elements. For a moving element whose appearance still matters, the documented approach includes checking the element region independently of its changed position.
  • Chromatic: add .chromatic-ignore or data-chromatic="ignore" to an element. Chromatic documents that the ignored element’s pixels, bounding box, and position are ignored, so do not apply this to a region whose size or placement you need to validate. It also supports disabling snapshots for a story.
  • Percy with Playwright: the client documents ignoreRegionSelectors, ignoreRegionXpaths, and custom rectangular boundaries for ignored regions in the percy-playwright documentation.

If only the text or image content changes while the page structure is what you want to assess, a layout-oriented mode may be appropriate. Applitools documents match modes for dynamic content. Choose the mode intentionally: it can tolerate changes that a pixel comparison would flag.

5. Keep coverage for important dynamic values

A visual mask is not a substitute for a functional check. If a masked region contains information that matters, assert it through the application’s accessible interface or data. Examples include checking that a status is “Active,” that a price is within the expected range, or that a timestamp is recent enough for the test’s purpose.

  • Mask the changing clock text, then assert the timestamp is parseable and within an allowed range.
  • Stub a rotating advertisement or third-party widget when its behavior is outside the page test’s scope.
  • Keep the container visible and checked if its dimensions or position matter; mask only the volatile child value.
  • When surrounding content shifts, use a tool-supported region or layout strategy that still checks the element’s appearance or structure.

This preserves two kinds of coverage: visual comparison for stable presentation and explicit assertions for dynamic meaning.

6. Diagnose failures in a useful order

  1. Different browser or host setup: compare the baseline and test browser, operating system, browser version, and capture mode. Align the environment before changing thresholds.
  2. Uncontrolled test data or third-party content: freeze or stub the input if practical. Otherwise mask only the unstable target and assert important values separately.
  3. Animations or transitions: wait for the intended state, use the framework’s animation handling, or neutralize specific effects with capture CSS when supported.
  4. A moving element with meaningful appearance: do not blanket-ignore it. Check the element independently or use a layout-aware strategy supported by your tool.
  5. An oversized ignore region: inspect its bounding box and shrink the target. In some tools, the region’s position and dimensions are ignored too.
  6. Many small differences: check fonts, image loading, viewport, device scale, and environment consistency before raising a global tolerance.
  7. A new baseline contains a real change: inspect and review the diff as an intentional code change. Do not accept every changed baseline automatically.

7. Troubleshooting common problems

Symptom Likely cause Fix
The same test produces different diffs on different machines. Browser, operating system, hardware, or headless settings differ. Use a consistent CI image and browser version for baseline creation and comparison.
The mask has no effect. The selector matches nothing at capture time, the element has not rendered yet, or the option is unavailable in the installed version. Wait for the element, verify the locator matches, and check the installed tool’s API documentation.
A whole card or section appears to be unchecked. The ignored locator or rectangle is broader than intended. Narrow it to the unstable child element and inspect what the tool excludes, including bounding-box behavior.
The page still fails because surrounding content moves. The volatile element changes layout, or the chosen mask does not suppress the relevant layout difference. Stabilize the input, reserve a fixed space if that matches the design, or use a tool feature intended for dynamic positioning.
A value is wrong but the screenshot passes. The value is masked or hidden, so its pixels are no longer checked. Add an explicit assertion for the value, or reduce the ignored area.
Raising the threshold makes unrelated regressions pass. A global tolerance is hiding small but meaningful changes. Restore a tighter threshold and stabilize or suppress the specific source of noise.
Capture CSS makes the layout look unlike the real page. The stylesheet removes elements or changes dimensions that affect surrounding content. Use a mask for the volatile pixels, or adjust CSS to preserve layout when that is the intended comparison.

8. Performance, reliability, and cost

Visual comparison cost is mainly affected by the number and size of captures, browser startup, page loading, and the comparison service or infrastructure your team uses. A mask usually changes what is compared; it does not make the browser skip loading or rendering the page. Stabilizing data and reusing a consistent browser setup can reduce flaky reruns and make failures easier to diagnose.

Keep screenshot scope appropriate. Full-page captures cover more content but can take longer and include more lazy-loaded or dynamic regions than a viewport capture. Wait for required content rather than adding a large arbitrary delay. Cache, parallelism, and retry behavior depend on your test runner and visual testing service; measure those settings in your own pipeline rather than assuming a universal speed or cost improvement.

For reliability, treat ignored regions and capture styles as test code: keep them small, named clearly, reviewed alongside the test, and periodically checked against the rendered page. A broad exclusion can make a suite appear stable while reducing what it verifies.

9. Capture a reference screenshot without browser setup

ScreenshotNeo is a website screenshot API and MCP server from ScreenshotNeo. It can capture a reference image, but it does not replace your visual regression tool’s baseline and diff workflow. Its API accepts common screenshot parameters, so it can also be useful when you want a clean capture without configuring a browser locally. See the ScreenshotNeo API documentation for options.

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,
)
r.raise_for_status()
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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));

Use your own target URL and API key. ScreenshotNeo can return PNG, JPEG, WebP, or PDF and supports capture options including full-page capture, CSS selectors, wait conditions, custom CSS, and hiding selectors. The API also reports page verdict and billing status in response headers. Its cookie-banner and popup cleanup is useful for clean reference captures, but a visual test should still use a consistent target state and assert any important dynamic content.

Or skip the browser setup

One GET request returns a screenshot:

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

Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. An 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. Sign up for the free plan.

10. Frequently asked questions

Does masking a locator test whether the hidden content is correct?

No. It removes that region from visual scrutiny. Add a separate functional assertion if the value matters.

Should I ignore a whole component if only its text changes?

Usually not. Mask or normalize the smallest changing part, and keep the component’s layout and styling under comparison.

Should I increase the pixel threshold to avoid flaky tests?

Only after checking capture stability and understanding the diff. A broader threshold can accept small real regressions across the image.

Can I use a screenshot API as my visual regression test?

A screenshot API can produce an image, but you still need a baseline and a comparison workflow to detect changes. Keep captures consistent and compare them with the tool that owns your visual test suite.