How to Hide Cookie Banners in Automated Website Screenshots
Hide a known cookie banner in Playwright screenshots with capture-only CSS, verify the result, and understand when masking or a screenshot API fits better.
To hide a known cookie banner in an automated screenshot, apply site-specific CSS during the screenshot operation. In Playwright, the style screenshot option lets you hide the matching element in the captured image without treating that visual change as a consent choice. Inspect the page to find the actual banner selector, take the screenshot, and verify that the banner is gone and the content beneath it is visible.
There is no universal cookie-banner selector. Consent tools use different markup, and a site can change its implementation. Hiding an element in a screenshot changes the image; it does not accept or reject cookies, save a preference, or otherwise record consent.
1. Hide the banner only in the screenshot
Use Playwright’s screenshot style option when the goal is a clean image and the page itself does not need to be changed for later interactions. The supplied stylesheet applies while the screenshot is taken. Playwright describes this option as useful for hiding dynamic elements or changing their properties to make screenshots repeatable. See the official Page API documentation.
Runnable Node.js example
Install Playwright and its Chromium browser in a new project:
npm init -y
npm install playwright
npx playwright install chromium
Save the following as screenshot.js. Replace the URL and example selector with values inspected on your target site.
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: true });
try {
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
await page.goto('https://example.com', {
waitUntil: 'domcontentloaded',
timeout: 30000,
});
await page.screenshot({
path: 'page-without-banner.png',
fullPage: true,
style: `
/* Replace this example with a selector from the target site. */
.cookie-banner {
display: none !important;
}
`,
});
} finally {
await browser.close();
}
})();
Run it with node screenshot.js. The example selector .cookie-banner is illustrative; it is not a selector Playwright or ScreenshotNeo guarantees to match.
Find and verify the selector
- Open the target page in a browser with its consent banner visible.
- Use the browser’s developer tools to inspect the banner and identify a selector specific enough to match the banner, but not a page-wide wrapper that also contains the content.
- Use that selector in the screenshot stylesheet. If the banner is inside an iframe, inspect the frame and test whether the screenshot stylesheet reaches the element in your Playwright version and setup.
- Open the resulting image and confirm the banner is gone, the page content remains, and no other elements were hidden.
For diagnosis, check how many elements the selector matches before capturing:
const matches = await page.locator('.cookie-banner').count();
console.log(`Matched ${matches} elements`);
A zero count suggests the selector is wrong, the banner has not appeared yet, or it lives in a frame or shadow root. A count greater than one may mean the selector is too broad. A count alone does not prove that screenshot-time CSS will affect the intended pixels, so inspect the image too.
2. Choose the right hiding method
| Method | Use it when | Behavior and caveat |
|---|---|---|
Screenshot style |
You only need the banner omitted from this screenshot. | CSS is applied for the capture. Start here for a capture-only change. |
page.addStyleTag |
Your page setup needs to inject CSS into the page or a frame. | It inserts a style tag or stylesheet. The page is modified for the current document rather than only at screenshot time. |
page.addInitScript |
Setup must run after document creation but before page scripts, including after navigation. | Runs on navigation and when child frames attach or navigate. Ordering between multiple context-level and page-level init scripts is undefined; do not depend on their relative order. |
Screenshot mask |
You want to cover the banner’s pixels, such as for a visual comparison. | Places a colored overlay over the matched element’s bounding box. It does not remove the banner and reveal the page beneath it. |
The documented APIs and their behavior are described in Playwright’s Page API and PageAssertions API. The assertion documentation describes screenshot styles that can pierce Shadow DOM and apply to inner frames; check the API and version used by your project before relying on that behavior.
Inject a stylesheet into the page
Use addStyleTag when the workflow calls for a page-level style injection. It can add CSS content or link a stylesheet. For example:
await page.addStyleTag({
content: '.cookie-banner { display: none !important; }',
});
await page.screenshot({ path: 'page.png', fullPage: true });
This changes rendered page styling for the current document. If you navigate, the document is replaced; add the style again when needed, or use an initialization approach for setup that must run before page scripts.
Run setup before page scripts
addInitScript is for initialization behavior, not the simplest way to hide a static, known banner. It runs after a document is created and before its page scripts run, including on navigation and when child frames attach or navigate. The API is documented in the Playwright Page API. Multiple scripts registered at context and page levels have undefined evaluation order, so keep required setup self-contained instead of depending on one init script preceding another.
Mask instead of hide
Playwright’s screenshot mask option overlays matched element bounding boxes with a color. That can help exclude pixels from a comparison image, but the page behind the banner is not revealed. Use CSS with display: none when the desired output should show the content underneath.
3. Handle timing, frames, and layout edge cases
- Late banner appearance: Consent interfaces may render after initial navigation. Wait for a relevant page state or the banner selector before capture, then confirm the rule matches. A fixed delay can help diagnose a race but is less reliable than waiting for a known condition.
- Single-page navigation: A client-side route change may render or re-render a consent component after the first document load. Capture after the route and relevant content are ready.
- Iframe or shadow root: A normal page locator or stylesheet may not reach every embedded component. Inspect the frame or shadow DOM and verify the documented behavior in the Playwright version being used.
- Multiple banners: A site may show both a consent panel and a separate preference or regional notice. Match only the elements intended for removal; avoid broad selectors such as
divor generic fixed-position rules. - Full-page screenshots: A banner positioned at the viewport edge can behave differently in a full-page capture than in a viewport screenshot. Inspect the complete output, including lower sections.
- Sticky overlays and backdrops: The visible panel may have a separate backdrop or scroll lock. Hide only the relevant panel and backdrop after inspecting their markup. Check that the page is not left with an unintended dim layer or disabled scrolling.
- Responsive layouts: The banner selector may be shared across desktop and mobile, or the site may render different markup. Verify each viewport you capture.
- CSS selector changes: A site redesign or consent-platform update can invalidate a selector. Treat selector matching and image review as part of ongoing capture maintenance.
4. Troubleshoot common failures
| Symptom | Likely cause | Fix |
|---|---|---|
| Banner remains in the image | The selector does not match, the banner rendered after capture, or it is isolated in a frame or shadow root. | Inspect the live DOM, check the locator count, wait for the relevant state, and test frame or shadow behavior. |
| Page content disappears too | The selector matches a shared container or too many elements. | Choose a narrower selector tied to the banner and inspect the match count and output. |
| Banner is gone but a dim overlay remains | The consent panel and backdrop are separate elements. | Inspect both elements and add a targeted rule for the backdrop if it should also be omitted. |
| Screenshot is captured before the page is ready | Navigation completion did not mean the application or consent component had finished rendering. | Wait for a page-specific element or state before taking the screenshot. Use a timeout appropriate to the site. |
| Injected CSS stops working after navigation | addStyleTag modified the previous document, which navigation replaced. |
Inject the stylesheet again after navigation, or use screenshot-time styling for a capture-only rule. |
| Mask covers the banner but does not reveal content | Mask is an overlay, not a hide rule. | Use screenshot CSS with display: none to reveal what lies behind it. |
| Screenshot looks different between runs | Dynamic content, delayed banners, viewport changes, or timing differences alter the rendered page. | Keep viewport and readiness conditions consistent; apply the same capture stylesheet and review changes when the site updates. |
5. Performance, reliability, and cost
A small CSS rule adds little work compared with starting a browser, loading the site, and rendering the screenshot. The main reliability risk is a selector or timing assumption becoming stale. Keep selectors narrow, use explicit page readiness conditions, set a navigation timeout, and inspect representative outputs after site or browser changes.
Self-hosted Playwright has no per-screenshot API charge from Playwright itself, but your browser runtime, compute, network, storage, retries, and maintenance have costs. At scale, account for concurrent browser capacity and avoid uncontrolled retries when a target site is slow. A screenshot API can remove browser installation and lifecycle management from your application; compare its documented behavior, billing rules, and options against your workload.
For ScreenshotNeo, the stated plans range from 1,000 screenshots a month free with no card to paid plans at $5 for 3,000 screenshots; higher plans are Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000. Yearly billing gives two months free. The provided product facts say only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and responses identify the page verdict and billing status in headers. Review current options and terms in the ScreenshotNeo documentation.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. For a one-call capture, supply your API key and target URL:
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)
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}`);
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets from 60+ known platforms before capture; each cleanup step can be turned off. Bot checks, blank pages, and failed loads are never billed. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. See the API documentation for request options and configuration.
Sign up free for 1,000 screenshots a month, with no card required.
6. Frequently asked questions
Does hiding the banner mean the site has recorded consent?
No. Screenshot CSS changes what appears in the image. It does not itself interact with the consent interface or record a consent choice.
Can I use one selector for every website?
No. Inspect each target and use a selector that matches its banner without hiding surrounding content.
Should I hide or mask a banner?
Hide it with CSS if you want to reveal the underlying page. Mask it if covering its pixels with an overlay is the intended result.
Will screenshot-time CSS change the page for later steps?
It is applied for the screenshot operation. Use page-level style injection when the workflow needs the page itself styled beyond that capture.


