ScreenshotNeo

BlogHow-to

How to monitor a website screenshot while ignoring one page element

Use Playwright Test’s mask option to exclude a changing element from screenshot comparisons, or use a screenshot stylesheet to hide it.

By the ScreenshotNeo team4 October 20268 min read

In Playwright Test, pass the locator for the changing element to the screenshot assertion’s mask option. Playwright covers that element’s bounding box with a pink rectangle in the comparison image, so changes inside the rectangle do not affect the visual comparison. The element is covered, not preserved and checked.

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

test('page screenshot ignores a dynamic element', async ({ page }) => {
  await page.goto('https://example.com');

  const changingElement = page.locator('[data-testid="live-value"]');
  await expect(page).toHaveScreenshot({
    mask: [changingElement],
  });
});

This example uses Playwright Test’s built-in visual assertion. It is not a general option for arbitrary screenshot code such as a standalone browser screenshot call. See the official PageAssertions API and visual comparison guide.

1. Set up a stable screenshot assertion

If you do not already have a Playwright Test project, install the test runner and its supported browsers, then create a test file:

npm init playwright@latest

Choose TypeScript or JavaScript when prompted. The example below is TypeScript. Save it in a Playwright test file such as tests/page-visual.spec.ts and run it with:

npx playwright test tests/page-visual.spec.ts

On the first run, Playwright creates the expected screenshot. Review and commit that baseline using your normal review process. Subsequent runs compare the new capture against it. The assertion waits until two consecutive page screenshots match before comparing with the expectation, and screenshot assertions disable animations by default. These behaviors help with capture stability, but they do not make changing application data deterministic. Set up the page state your test intends to check before taking the screenshot.

2. Choose a locator for the element to ignore

Use a locator that consistently identifies only the volatile element. A test ID is often a good choice when your application provides one:

const changingElement = page.getByTestId('live-value');
await expect(page).toHaveScreenshot({ mask: [changingElement] });

You can also use a CSS selector when it is stable and specific:

const changingElement = page.locator('.stock-price');
await expect(page).toHaveScreenshot({ mask: [changingElement] });

The mask option takes an array of Playwright locators. You can mask several elements in one assertion:

await expect(page).toHaveScreenshot({
  mask: [
    page.getByTestId('live-value'),
    page.locator('[data-testid="updated-at"]'),
  ],
});

Prefer a semantic or otherwise stable locator over coordinates. A locator follows the intended page element when layout changes; coordinates can end up covering a different part of the page after a layout shift. If a locator matches more than one element, narrow it to the intended target so you do not hide more of the page than planned.

3. Understand what the mask changes

By default, Playwright paints a pink box (#FF00FF) over each matched element’s bounding box in the screenshot used for comparison. This suppresses visual variation in that area. It also means you are no longer checking the covered element’s appearance: a regression inside the masked area can go unnoticed. Keep the masked region as small and specific as practical.

The mask is an overlay for the screenshot assertion. Use it when you want to ignore visual differences in a region while leaving the page itself in its normal state. If you want the element removed from the capture instead, use a screenshot stylesheet.

4. Hide or change an element with a screenshot stylesheet

Playwright’s screenshot assertion supports stylePath, which applies a stylesheet during capture. This can hide a volatile element or change its screenshot-only appearance:

/* tests/screenshot.css */
[data-testid="live-value"] {
  visibility: hidden !important;
}
import { test, expect } from '@playwright/test';

test('page screenshot hides a dynamic element', async ({ page }) => {
  await page.goto('https://example.com');
  await expect(page).toHaveScreenshot({
    stylePath: 'tests/screenshot.css',
  });
});

Use a selector that targets only the intended element. The page is visually altered for this screenshot, so the baseline will not show that element. The stylesheet is useful when you want the surrounding page to remain visible without a colored mask, but it likewise means changes to the hidden element are not being checked.

Choose between the approaches based on the expected image:

Approach What appears in the capture Use it when
mask A solid pink rectangle covers the locator’s bounding box. You want to ignore changing pixels while making the excluded region obvious in the comparison.
stylePath The screenshot stylesheet hides or changes the selected element. You want the element removed or visually adjusted in the expected screenshot.

5. Keep the rest of the screenshot meaningful

  • Mask only genuinely volatile content. Excluding a region also excludes visual regressions in that region.
  • Keep the locator specific. Broad selectors can hide more than one element or conceal useful page content.
  • Make the page state repeatable. Use controlled test data and a predictable route or state when possible; a mask does not stabilize the rest of the page.
  • Review baseline changes. A new baseline should represent an intended UI change, not merely silence a failing comparison.
  • Do not treat the mask as an assertion about the element. Add a separate test if the dynamic element’s content or behavior matters.

6. Troubleshoot common failures

Symptom Likely cause What to do
The dynamic region still causes screenshot differences. The locator does not match the changing element, or it matches a different element than intended. Check the selector against the rendered page and use a stable, specific locator, such as a test ID.
Too much of the page is covered. The locator matches multiple elements or selects a large parent container. Narrow the locator to the one changing element and inspect its bounding box in the comparison image.
The baseline contains the pink rectangle. The screenshot assertion is using mask, which covers the target with a pink overlay by default. This is the expected mask behavior. Use stylePath if you want to hide or visually change the element instead.
The element is absent from every screenshot. A screenshot stylesheet hides it, or the application does not render it in the test state. Check the stylesheet selector and the test’s page state. Remove or adjust the screenshot-only CSS if the element should appear.
Screenshot comparisons still vary outside the ignored area. Other page content is changing, or the application state is not repeatable. Identify the remaining changing regions and make the test state deterministic. Mask only the regions that are intentionally out of scope.
The screenshot assertion or option is unavailable. The test is not using Playwright Test’s page screenshot assertion. Use expect(page).toHaveScreenshot() in a Playwright Test test, or use the ignore-region feature documented by the visual-testing service in your workflow.

7. Other visual-testing workflows

If you need a broader hosted visual-testing workflow, these integrations document their own ways to exclude regions. Applitools Eyes for Playwright documents ignoreRegions with a page locator and also supports checking a selected region or element. The Percy Playwright client documents ignore regions selected with CSS selectors, XPath, or custom coordinate boundaries. These are optional workflows; Playwright Test’s built-in mask option is enough for a one-element exclusion in its screenshot assertion.

8. Capture a screenshot without writing browser setup

For a screenshot you need to fetch from an application or script, a screenshot API can avoid setting up and maintaining browser capture code. ScreenshotNeo is a website screenshot API and MCP server for developers. It accepts a URL in one GET request and returns an image or PDF. Its API parameters also work with the names used by other screenshot APIs, which can make switching easier. Read the ScreenshotNeo API documentation.

cURL

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

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.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));

These calls capture a URL; they do not configure Playwright Test’s screenshot comparison, mask a locator, or create and compare a visual baseline. Use the Playwright approach above when the goal is to ignore one element in a test comparison.

Options for capture workflows

ScreenshotNeo supports full-page capture with lazy images loaded, capturing an element by CSS selector, dark mode, 12 device presets and custom viewports, retina scale, PDF settings, HTML or CSS to image, custom CSS and JavaScript, clicking an element before capture, hiding selectors, and waits for a selector, delay, or network idle. It also supports request and resource blocking, custom headers, cookies, user agent and Authorization, timezone and geolocation, transparent backgrounds, image resizing, caching with a chosen TTL, signed links for public image tags, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. See the docs for request parameters and usage details.

Or skip the browser setup

Make a GET request to https://api.screenshotneo.com/v1/shot with your access key and target URL, as in the examples above. ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for 1,000 free screenshots a month, with no card required.

9. Performance, reliability, and cost

For Playwright screenshot tests, capture time depends on navigating to the page, reaching the intended state, and the screenshot assertion’s stability wait. Keep each test focused, avoid waiting on unrelated work, and make the page state repeatable. Playwright’s documented wait for two consecutive matching screenshots can help avoid comparing a transient frame; it does not guarantee that dynamic data elsewhere has stopped changing.

For hosted URL capture, the amount of work depends on the page and the requested capture options. Choose the smallest capture that answers your need, and use caching when a cached result is acceptable. ScreenshotNeo returns verdict and billing headers so a caller can distinguish clean captures from cases that are not billed. Its listed monthly plans are Free: 1,000 shots; Starter: $5 for 3,000; Growth: $15 for 15,000; Pro: $39 for 60,000; Scale: $99 for 250,000; Business: $249 for 1,000,000. Yearly billing gives two months free, and every feature is available on every plan.

FAQ

Does mask remove the element from the page?

No. It covers the locator’s bounding box in the screenshot used by the assertion. Use stylePath to hide or change an element for the capture.

Can I use mask with a standalone Playwright screenshot?

The documented mask option described here belongs to Playwright Test’s toHaveScreenshot assertion. It is not a general option for every screenshot call.

Will Playwright mask an element that appears later?

The locator must identify the element when the assertion captures the page. If the target is rendered asynchronously, wait for the intended page state before taking the screenshot.

Should I mask a value that the test needs to validate?

No. Masking means its appearance is not checked by that screenshot comparison. Validate important content separately, then exclude it from the visual assertion if its changing appearance is expected.