Puppeteer Screenshot Shows a Cookie Banner: How to Dismiss It First
Find the consent control your target page actually renders, click it with Puppeteer, verify the banner is gone, then capture a clean screenshot.
To dismiss a cookie banner before a Puppeteer screenshot, click the consent control rendered by that specific site, wait until its banner is hidden, and then call page.screenshot(). A banner built into the webpage is a normal DOM element. Puppeteer’s Dialog API is for browser-native JavaScript alerts, prompts, and confirms, not ordinary HTML consent overlays. Puppeteer recommends locators for interaction because they wait for an element and check that it is ready for the requested action. Puppeteer page interactions · Puppeteer Dialog API
1. Inspect the banner and choose a real control
There is no universal cookie-banner selector or universal button label. Sites can render different consent tools, labels, markup, and timing. Inspect the target page in a browser or with Puppeteer’s DOM inspection tools. Identify both:
- A control that expresses the action you intend, such as accepting, rejecting, or managing consent.
- A selector for the banner or overlay that can confirm it disappeared.
Use an accessible name or stable site-specific attribute when available. A selector based only on a generic button can match unrelated controls. A text match can also be ambiguous: inspect matches and scope the button to the consent container when the page has multiple similar labels. Puppeteer accepts CSS selectors and also supports text, accessibility, XPath, and open Shadow DOM selector syntax. Selector and locator guide
Choose the appropriate consent action for your purpose and the site’s rules. “Dismiss” is not always the same as “accept”: if your task requires rejecting optional cookies, click that specific control rather than choosing the easiest button.
2. Runnable Puppeteer example
This Node.js script demonstrates the full order: navigate, click a site-specific consent button, confirm the banner is hidden, and save the screenshot. Replace TARGET_URL, CONSENT_BUTTON_SELECTOR, and BANNER_SELECTOR with values observed on your page. The example assumes the control and banner are in the main document, and that the button click hides the banner.
// Save as screenshot.mjs
// Install Puppeteer with: npm install puppeteer
import puppeteer from 'puppeteer';
const targetUrl = process.env.TARGET_URL ?? 'https://example.com';
// These are placeholders, not Puppeteer or industry-standard selectors.
const consentButtonSelector = process.env.CONSENT_BUTTON_SELECTOR ?? '[data-consent-accept]';
const bannerSelector = process.env.BANNER_SELECTOR ?? '[data-consent-banner]';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
page.setDefaultTimeout(10_000);
page.setDefaultNavigationTimeout(30_000);
// Pick a waitUntil mode suitable for this site. A load event does not
// guarantee that a consent tool injected later has appeared.
await page.goto(targetUrl, { waitUntil: 'domcontentloaded' });
// Locator waits for the element to be present and ready to click.
await page.locator(consentButtonSelector).click();
// Do not capture a misleading image if the overlay remains.
await page.waitForSelector(bannerSelector, { hidden: true, timeout: 10_000 });
await page.screenshot({ path: 'page.png', fullPage: true });
console.log('Saved page.png');
} finally {
await browser.close();
}
page.locator() is the recommended interaction API. Its click waits for the target to be present, visible, enabled, in the viewport, and stable before acting. The exact defaults and available APIs can vary with the Puppeteer version in your project; consult the documentation matching the version you install. The official pages linked here identify version 25.12.0. Locator behavior · Screenshot guide
Match a button by its visible text
If the page has an accessible, stable label but no reliable attribute, a locator filter can match text. This example expects a button whose trimmed text is exactly Accept; adapt it to the actual page and avoid using it if multiple buttons match.
await page
.locator('button')
.filter(button => button.textContent?.trim() === 'Accept')
.click();
await page.waitForSelector('[data-cookie-banner]', { hidden: true });
await page.screenshot({ path: 'page.png' });
The text and [data-cookie-banner] selector above are illustrative only. Puppeteer’s locator filter callback runs in the browser context. For a reusable helper, inspect the target page first and prefer a selector scoped to the consent dialog rather than blindly clicking the first button that happens to say “Accept.”
Wait for the banner if it loads asynchronously
If the consent UI appears after the initial document, explicitly wait for its selector before clicking. Waiting for domcontentloaded or load does not prove that a third-party script has finished injecting its UI. The right navigation and element waits depend on the site; there is no universal wait condition for consent banners.
await page.goto(targetUrl, { waitUntil: 'domcontentloaded' });
await page.waitForSelector(bannerSelector, { visible: true, timeout: 10_000 });
await page.locator(consentButtonSelector).click();
await page.waitForSelector(bannerSelector, { hidden: true, timeout: 10_000 });
await page.screenshot({ path: 'page.png' });
waitForSelector supports visibility and hidden-state waits. If hidden-state verification times out, treat that as a failed dismissal and inspect the page instead of saving an image that may still contain the overlay. Page.waitForSelector()
Capture only an element after dismissing the overlay
When the goal is a component screenshot rather than a full-page image, dismiss and verify the banner first, then capture the target element:
await page.locator(consentButtonSelector).click();
await page.waitForSelector(bannerSelector, { hidden: true });
const card = await page.waitForSelector('#pricing-card', { visible: true });
if (!card) throw new Error('Pricing card was not found');
await card.screenshot({ path: 'pricing-card.png' });
await card.dispose();
Replace #pricing-card with the element you need. Puppeteer supports both page screenshots and element screenshots. Screenshot guide
3. Handle native JavaScript dialogs separately
If the page opens a browser-native alert(), confirm(), or prompt(), register a dialog handler. This is a different case from an in-page cookie banner. Decide whether accepting or dismissing that dialog is correct for your workflow.
page.on('dialog', async dialog => {
console.log(`Native dialog: ${dialog.type()} — ${dialog.message()}`);
await dialog.dismiss();
});
await page.goto(targetUrl);
await page.screenshot({ path: 'page.png' });
Do not add this handler expecting it to remove an HTML overlay: DOM consent interfaces are handled with locators/selectors and a visibility check. Puppeteer’s Dialog API exposes dialog methods for the browser-native case. Dialog API
4. Selector and wait choices
| Choice | Use it when | Watch for |
|---|---|---|
| CSS attribute or ID selector | The site exposes a stable consent attribute or container ID. | Classes generated by a build system may change; inspect and maintain selectors. |
| Accessible name or text locator | The consent button has a clear, stable label. | Duplicate labels, localization, capitalization, or wording changes can cause ambiguity. |
| Wait for banner visible, click, wait hidden | The banner is injected asynchronously and must be removed before capture. | Set timeouts deliberately; fail visibly if it never appears or does not disappear. |
| Native dialog event | A browser JavaScript dialog blocks page interaction. | This does not target ordinary DOM banners. |
A locator action waits for readiness to perform its action; it does not establish that the consent overlay has been dismissed after the click. Follow the click with a wait for the banner’s actual hidden state. Avoid an arbitrary fixed sleep when a selector-based condition is available: a sleep can waste time on quick pages and still be too short on slow ones.
5. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Screenshot still shows the banner | The click targeted the wrong control, did not run, or closed a settings panel while leaving the banner present. | Inspect the rendered consent UI, verify the intended button, and wait for the banner container to become hidden before capture. |
| Locator times out before clicking | The selector is wrong, the banner has not loaded, or the element is inside a frame or shadow root. | Confirm the selector against the live DOM; wait for the banner; inspect frames; for open Shadow DOM use Puppeteer’s deep selector syntax where appropriate. |
| Click throws because the target is not actionable | The element may be disabled, covered, moving, outside the viewport, or not the button you expected. | Use a locator and inspect the page state. Confirm the control is visible and enabled; avoid forcing a click that bypasses actionability checks unless you understand why the normal click cannot proceed. |
| Banner appears again after navigation | Consent may be stored in browser storage or a cookie, or the site may show the banner on every route/session. | Click and verify on each fresh context, or deliberately seed the correct site state when your test setup permits it. Do not assume a click persists consent; persistence is site-specific. |
| Text-based selector clicks the wrong button | Several page buttons share the same label. | Scope the locator to the consent dialog/container and inspect the match before automating repeated captures. |
| Banner is inside an iframe | The consent vendor rendered its UI in a child frame rather than the main document. | Find the relevant frame and use its locator, then verify the frame’s banner state. Main-page CSS queries do not cross iframe boundaries. |
| Click completes but the banner stays visible | The click may open preferences, require another choice, or update state asynchronously. | Inspect the resulting UI and perform the next intended interaction; wait for the actual container to hide rather than assuming one click is enough. |
| Navigation fails or content is incomplete | The site may be slow, redirect, require authentication, or never reach a chosen network-idle state. | Choose a navigation wait that fits the site, set a navigation timeout, and wait for the specific content needed. Do not treat a timeout as a successful capture. |
6. Reliability, performance, and cost
- Make selector failures explicit. A missing consent control or a banner that remains visible should fail the job or be recorded as an exception, not silently produce a misleading “clean” image.
- Keep waits condition-based. Wait for the banner and its hidden state, and for any page content required in the screenshot. Avoid increasing every timeout without evidence; excessive waits make batches slower and obscure genuine failures.
- Reuse browser processes carefully. For repeated captures, reusing a launched browser can avoid repeated startup work. Use an appropriate isolated browser context for each independent session so cookies and storage do not leak between targets or test cases.
- Expect site variation. Banner markup, timing, localization, frames, shadow roots, and consent persistence can differ. Test your selectors against the actual URLs and viewport sizes you capture.
- Account for operational cost. A DIY flow uses your own browser runtime and infrastructure, so you manage browser installation, concurrency, retries, storage, and maintenance. Puppeteer documentation does not establish a universal runtime or cost benchmark for this task; measure against your pages and deployment environment.
- Do not mistake dismissal for consent policy. Clicking an accept control changes the page’s consent state. Select the action that is appropriate for the workflow, and do not fabricate consent state to bypass a site’s intended experience.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server by Yorker Media. A single request returns an image or PDF; it can accept cookie/consent banners like a visitor and remove 60+ known consent platforms, newsletter popups, and chat widgets before capture. Each step can be turned off. Bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server gives AI agents tools for screenshots, page info, and PDF capture.
Install Python’s requests package first. Replace the URL and API key, then run this script. See the ScreenshotNeo API documentation for options and configuration.
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)
For a direct shell request, save the response as an image:
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
Or call it from 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 require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
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; and 1,000 screenshots a month are free with no card. Paid plans start at $5 for 3,000. Get 1,000 free screenshots a month with no card.
FAQ
Why does my Puppeteer screenshot still show a cookie popup?
The page may not have loaded the consent UI when the script looked for it, the selector may target the wrong control, or the click may not have fully dismissed the banner. Verify the real control and wait for the banner container to become hidden before taking the screenshot.
Can I remove every cookie banner with one generic selector?
No. Consent interfaces vary by site, and the right control depends on the page’s markup and the choice your workflow requires. Inspect each target and use site-specific selectors.
Does Puppeteer’s dialog handler dismiss cookie banners?
Only if the prompt is a browser-native JavaScript dialog. A banner rendered in the page is a DOM element and should be handled with a locator or selector.
Does clicking Accept guarantee consent will persist?
No universal guarantee applies. Persistence depends on the site’s behavior and browser storage. Verify the result in the context and session you use for later captures.


