ScreenshotNeo

BlogHow-to

How to Hide Cookie Banners and Popups in Playwright Screenshots

Use screenshot-time CSS to omit overlays from an image, or dismiss them through the page when your test needs to exercise consent behavior.

By the ScreenshotNeo team4 October 20269 min read

For a clean, one-off Playwright screenshot, pass a narrow CSS rule in page.screenshot({ style }). It hides the matching banner or popup for the capture without saving a consent choice. If the test is meant to verify consent behavior, use the site’s actual controls and assert the result instead.

There is no universal cookie-banner selector. Inspect the target page and choose a selector that identifies only the overlay you mean to remove. The official Playwright Page API documents capture-time styles, screenshot masks, animation handling, and locator handlers.

1. Choose the right approach

Goal Approach What it does
Get a clean image page.screenshot({ style }) Applies CSS for the screenshot so matched overlays are absent from the image.
Test consent behavior Click an accept, reject, or settings control, then assert the resulting state Exercises the site’s real consent flow. Hiding the banner alone does not accept or reject consent.
Dismiss a predictable popup before an action Wait for it and dismiss it in the normal test flow Makes the expected interaction explicit and keeps the test’s steps understandable.
Handle an unexpected overlay blocking an action page.addLocatorHandler() Runs a handler when Playwright encounters the matching locator during an actionability or auto-wait check.
Cover sensitive content Screenshot mask Covers the matched element’s bounding box with a colored rectangle. It does not make the element blend into the page.

Playwright recommends explicitly waiting for and dismissing predictable overlays as part of the normal flow, rather than using addLocatorHandler(). A handler does not monitor the page continuously; it is invoked in connection with actions or assertions that perform relevant checks. See the locator handler documentation.

2. Hide an overlay only in the screenshot

Install Playwright, save the following as capture.mjs, and run it with node capture.mjs. Replace the example URL and placeholder selectors with the page and selectors you inspected.

npm install playwright
npx playwright install chromium
// capture.mjs
import { chromium } from 'playwright';

const url = process.env.TARGET_URL ?? 'https://example.com';
const browser = await chromium.launch({ headless: true });

try {
  const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
  await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30_000 });

  await page.screenshot({
    path: 'page.png',
    fullPage: true,
    style: `
      #cookie-banner,
      [data-testid="cookie-banner"],
      .newsletter-modal {
        display: none !important;
      }
    `,
    animations: 'disabled',
  });
} finally {
  await browser.close();
}

Run it against another page with TARGET_URL=https://your-site.example node capture.mjs. The selector names above are examples, not selectors Playwright or ScreenshotNeo guarantee to find. Remove selectors you do not need and avoid broad rules such as div { display: none }, which can hide page content.

Why use style?

The screenshot option applies stylesheet text while Playwright captures the image. That keeps a visual-only change scoped to the screenshot. By comparison, page.addStyleTag({ content }) adds a style tag to the page; use it when you deliberately want the injected style to remain present for subsequent actions or captures. Both methods still depend on selectors that match the page.

What to check after hiding the overlay

  1. Inspect the page’s DOM and identify the smallest stable selector for the banner or popup.
  2. Capture the screenshot, then inspect the resulting image rather than assuming a selector match means the layout looks right.
  3. If the site reserves space for a removed banner, adjust a site-specific spacing rule only when the output requires it.
  4. Check the viewport and full-page image. Removing a fixed or sticky overlay can change what is visible without removing the space or layout behavior the site created.

When consent is part of the behavior under test, interact with the real control and assert a visible outcome. Use an accessible role and name where possible, or a stable test ID when that is the site’s explicit test contract. The exact button name and post-consent assertion are site-specific.

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

test('accepting consent closes the banner', async ({ page }) => {
  await page.goto('https://your-site.example');

  const banner = page.getByTestId('cookie-banner');
  await expect(banner).toBeVisible();

  await banner.getByRole('button', { name: 'Accept all' }).click();
  await expect(banner).toBeHidden();

  // Assert a meaningful consent result for your own site here, such as
  // a preferences panel state or a consent-dependent page element.
});

For a predictable newsletter dialog that blocks a known action, dismiss it explicitly before continuing:

await page.goto('https://your-site.example');

const closeButton = page.getByRole('button', { name: 'No thanks' });
if (await closeButton.isVisible().catch(() => false)) {
  await closeButton.click();
}

await page.getByRole('link', { name: 'Continue' }).click();

For a genuinely unexpected overlay that may appear while another action runs, a locator handler can help:

const popup = page.getByText('Sign up to our newsletter');

await page.addLocatorHandler(popup, async () => {
  await page.getByRole('button', { name: 'No thanks' }).click();
});

await page.getByRole('button', { name: 'Start here' }).click();

Do not use a handler as a substitute for consent assertions: it dismisses a match when checks encounter it; it does not prove the site’s consent behavior is correct.

4. Use masks, animations, and frames appropriately

Mask an element when a visible cover is acceptable

await page.screenshot({
  path: 'masked.png',
  mask: [page.getByTestId('account-number')],
  maskColor: '#333333',
});

A mask overlays the element’s bounding box with a solid color (pink by default). This is useful when the goal is to obscure sensitive content, but it generally will not look like the page had no banner. For a natural-looking clean shot, use screenshot-time CSS instead. Details are in the screenshot API.

Disable motion for repeatable captures

The screenshot option animations: 'disabled' can reduce unrelated visual changes. Playwright fast-forwards finite animations and cancels infinite animations for the capture; it does not remove overlays. If an overlay animates in, a narrow style rule can hide it while the image is taken.

Handle overlays inside an iframe

A regular page selector searches the main document, not the contents of a separate frame. Use a frame locator when you need to interact with an element inside a known iframe:

const consentFrame = page.frameLocator('#consent-frame');
await consentFrame.getByRole('button', { name: 'Accept all' }).click();

Playwright’s screenshot style documentation says capture-time styles apply to inner frames. Still inspect the captured image and verify your selector against the actual frame content. See FrameLocator.

5. Screenshot options that affect the result

Option Use Important detail
style Hide known elements with CSS for this capture Use a narrow selector; no universal banner selector exists.
fullPage Capture the full scrollable page Defaults to false; a full-page image can reveal content below a viewport overlay.
animations Reduce motion-related screenshot differences 'disabled' changes finite and infinite animations for capture; it is not an overlay-removal method.
mask, maskColor Cover matching elements Produces a colored rectangle over the bounding box. The default is pink; the color can be customized.
path Save the image to a file The extension determines the screenshot type when supported.
quality Set JPEG or WebP quality Does not apply to PNG; check the API for format-specific behavior.
scale Choose CSS or device scale for pixels Higher pixel output can increase image size and capture work.

Consult the current Page screenshot options for the complete option list and version-specific details. Screenshot assertions such as toHaveScreenshot() belong to the Playwright test runner; see PageAssertions.

6. Troubleshooting

Symptom Likely cause Fix
The banner remains in the image The selector does not match, or the element is in a frame or shadow tree. Inspect the live DOM, confirm the selector, and check frame placement. Playwright documents that screenshot styles pierce Shadow DOM and apply to inner frames; verify the actual output.
The rule hides real page content The selector is too broad or matches multiple elements. Scope it to a stable banner ID, test ID, or specific container; remove unrelated selectors and inspect the screenshot.
There is an empty gap where the banner was The site may reserve layout space separately from the overlay. Inspect the page structure and add only the necessary site-specific layout adjustment to the capture CSS.
The banner returns after reload Screenshot-time styling is temporary and does not save consent. For a persistence test, use the real consent control and check the behavior after navigation or reload.
The locator handler does not run No action or assertion has triggered a relevant locator check, or the handler locator does not match. Use explicit waits and dismissal for a predictable popup. For a handler, ensure an action or auto-waiting assertion encounters its locator.
The masked area is bright pink That is the documented default mask color. Set maskColor if a cover is desired, or use screenshot-time CSS if the element should be absent from the image.
The test passes but consent was never recorded The test hid or dismissed the UI without asserting the site’s resulting consent state. Click the actual consent choice and assert a site-specific visible state or other behavior that proves the choice took effect.
The capture is inconsistent between runs Animations, delayed overlays, changing content, or a different page state may affect the result. Use a deterministic test state, wait for the relevant page content, disable animations where appropriate, and keep the CSS selector narrow.

7. Performance, reliability, and cost

For a single local capture, CSS injection adds little complexity compared with launching a browser and loading the page; the browser navigation and rendering work remain the main steps. Reuse a browser or context for batches of captures where your test architecture allows it, and close them reliably so failed captures do not leave browser processes behind.

Reliability depends on the stability of the page and selector. Prefer a site-owned test ID or other stable attribute over a guessed vendor-specific class. Keep screenshot dimensions and page state consistent for visual comparisons. If you use screenshot tests, follow the installed Playwright version and keep expected screenshots updated intentionally when the design changes.

With the DIY approach, you run and maintain the browser environment and its dependencies. For occasional work this can be a good fit; for recurring captures, factor in browser setup, execution time, storage, and CI maintenance. Playwright itself is the documented solution here; this article makes no performance benchmark claim.

Or skip the browser setup

ScreenshotNeo is a website screenshot API: one GET request returns an image or PDF. Its capture flow 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, and failed loads are never billed. It also has an MCP server for AI agents, including Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. See the ScreenshotNeo API documentation.

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

Get an API key and start with 1,000 free screenshots a month, with no card required.

8. Frequently asked questions

No. Capture-time CSS changes the screenshot appearance; it does not record a consent choice.

Can I hide every popup with one built-in Playwright option?

No universal selector or popup-removal switch is documented. Identify the specific elements on the page and choose CSS, a real dismissal action, or a handler according to the goal.

Use a mask when a solid cover is acceptable. Use CSS when you want the screenshot to show the page as if the matched element were absent.

Will screenshot-time CSS persist across navigation?

No. It is applied during capture, so use the site’s controls when testing state that should persist.