ScreenshotNeo

BlogHow-to

How to Capture a Web Page Screenshot After Dismissing a Consent Dialog

Dismiss a cookie banner or consent modal, verify the page is ready, and capture it with Playwright or Puppeteer. Includes runnable examples and troubleshooting.

By the ScreenshotNeo team4 October 20269 min read

To capture a page after dismissing a consent dialog, wait for the consent control, activate the intended choice, confirm the overlay is gone, and then take the screenshot. A cookie banner or consent modal is usually part of the page, so interact with its button or other page control. A browser-native JavaScript dialog such as alert() or confirm() uses a separate automation API.

The button label and selector depend on the site. Choose the action you actually intend—accept, reject, or necessary cookies—and replace the example locator below with one that matches the target page. The Playwright Page API recommends explicitly waiting for a predictable overlay and dismissing it in the normal flow.

This runnable Node.js example accepts a target URL, waits for a site-specific consent button, clicks it, waits for it to disappear, and captures the full page. Install Playwright with npm install playwright; install its browser if needed with npx playwright install chromium. Save the code as capture.js and run node capture.js https://example.com.

const { chromium } = require('playwright');

async function main() {
  const url = process.argv[2];
  if (!url) throw new Error('Usage: node capture.js <url>');

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

    // Replace this illustrative locator with the intended control on this site.
    const consentButton = page.getByRole('button', {
      name: /accept all|reject all|necessary only|agree/i
    });
    await consentButton.waitFor({ state: 'visible', timeout: 10_000 });
    await consentButton.click();
    await consentButton.waitFor({ state: 'hidden', timeout: 10_000 });

    // Add a site-specific readiness check here if content loads after consent.
    await page.screenshot({ path: 'page.png', fullPage: true });
  } finally {
    await browser.close();
  }
}

main().catch(error => {
  console.error(error);
  process.exitCode = 1;
});

domcontentloaded means the initial document was parsed; it does not prove that the consent UI or all page content is ready. Waiting for the actual control provides that synchronization. If consent reveals content asynchronously, wait for a meaningful content selector before the screenshot.

Do not blindly click the first button containing “accept” if your capture needs a different consent state. Inspect the site and use an accessible role and name when available. For example, a site may have separate “Accept all” and “Reject optional” buttons. Target the one that matches the state you need, and keep that choice explicit in your script.

Consent choices may persist in cookies or local storage. A fresh browser context is useful when you need to test the first-visit dialog each run. Reuse a context or saved state only when you want the choice to persist between captures and have permission to retain that state.

2. Capture only a specific element

For a card, chart, or other component, use a locator screenshot instead of capturing the full page. The element must exist and be visible after consent is dismissed.

const card = page.locator('[data-testid="report-card"]');
await card.waitFor({ state: 'visible' });
await card.screenshot({ path: 'report-card.png' });

Replace [data-testid="report-card"] with a selector from the page. A stable test attribute is generally preferable to a long chain of layout-dependent CSS selectors. Playwright documents page screenshots and element interactions in its Page API.

3. Handle browser-native JavaScript dialogs separately

A native alert, confirm, prompt, or beforeunload prompt is not an HTML cookie banner. Register a dialog handler before the action that might trigger it. The following example dismisses native dialogs; change dialog.dismiss() to dialog.accept() if the intended behavior is to accept.

page.on('dialog', async dialog => {
  console.log(`Native dialog: ${dialog.type()}`);
  await dialog.dismiss();
});

// Perform the action that may trigger alert(), confirm(), or prompt() here.
// Then capture after the page reaches the state you expect.

Without a handler, a native dialog can stall the action waiting for it. A registered handler must accept or dismiss it. See Playwright dialog documentation. This mechanism does not click a consent button rendered in the page.

4. Puppeteer version

If your project already uses Puppeteer, follow the same sequence: navigate, wait for the site’s actual consent control, click it, verify it has gone, and capture. Install with npm install puppeteer. Save as capture-puppeteer.js and run it with a URL argument.

const puppeteer = require('puppeteer');

async function main() {
  const url = process.argv[2];
  if (!url) throw new Error('Usage: node capture-puppeteer.js <url>');

  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    await page.setViewport({ width: 1440, height: 900 });
    await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30_000 });

    // Replace the selector and text with the site's intended consent action.
    const selector = 'button';
    await page.waitForSelector(selector, { visible: true, timeout: 10_000 });
    const buttons = await page.$$(selector);
    let clicked = false;
    for (const button of buttons) {
      const text = (await page.evaluate(el => el.innerText, button)).trim();
      if (/^(accept all|reject all|necessary only|agree)$/i.test(text)) {
        await button.click();
        clicked = true;
        break;
      }
    }
    if (!clicked) throw new Error('Consent action button was not found; inspect the page and update the selector/text.');

    // Replace this condition with a site-specific check if the banner remains in the DOM.
    await page.waitForFunction(() => {
      const visible = el => !!el && !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);
      return ![...document.querySelectorAll('button')].some(el => visible(el) && /^(accept all|reject all|necessary only|agree)$/i.test(el.innerText.trim()));
    }, { timeout: 10_000 });
    await page.screenshot({ path: 'page.png', fullPage: true });
  } finally {
    await browser.close();
  }
}

main().catch(error => {
  console.error(error);
  process.exitCode = 1;
});

The text matching in this example is intentionally narrow and illustrative. If the page has multiple similar buttons, a dialog inside an iframe, or an unusual consent widget, inspect the DOM and target that widget directly. Puppeteer documents Page.screenshot() for pages and ElementHandle.screenshot() for an element; its screenshot guide includes navigation and capture examples.

5. Make the capture reliable

  • Wait for the state you need. Navigation completing does not guarantee that a consent control is visible or that post-consent content has loaded. Wait for the consent control first, then for a page-specific content marker if required.
  • Verify dismissal. Wait for the overlay or its control to become hidden, or check that the obstructing element is no longer visible. Some sites remove the element; others hide it or replace it.
  • Use a deliberate readiness condition. A fixed delay can help with a known animation, but it is less reliable than waiting for a visible element or a known page state. Puppeteer’s guide uses a network-idle navigation condition as an example; it is not a universal readiness guarantee for sites with ongoing requests.
  • Keep browser state intentional. Use a fresh context for first-visit behavior. Reuse cookies or storage only when you want a remembered consent state.
  • Choose the capture scope. Use a page screenshot for the full page and an element screenshot for a component. Confirm viewport dimensions and full-page behavior match the result you need.
  • Clean up browser resources. Close pages and browser processes in a finally block so an exception does not leave automation processes running.

6. Troubleshooting

Symptom Likely cause Fix
Consent button wait times out The selector or accessible name does not match, the dialog is delayed, or it is inside an iframe. Inspect the rendered page, wait for the actual control, and use a frame locator for an iframe. Do not assume one selector works across sites.
Click succeeds but banner remains The click hit a different control, opened preferences, or the overlay uses a separate close action. Target the intended action by role/name or verified selector, then wait for the overlay itself to disappear.
Screenshot still shows the dialog Capture ran before dismissal completed, or the dialog was recreated. Wait for the overlay to be hidden and check the page’s resulting state before capturing.
Content is missing after dismissal The page loads content asynchronously or requires another state transition. Wait for a meaningful content selector or application state after clicking; navigation completion alone may be insufficient.
Click is blocked or intercepted An animation, another overlay, or a sticky element covers the control. Wait for the transition, identify the covering element, and interact with the real visible control. Avoid forcing a click until you understand why normal interaction fails.
Run hangs on a JavaScript prompt A native dialog is open and has no handler. Register the framework’s dialog event listener and explicitly accept or dismiss it.
Works once but not on the next run Consent state was persisted in cookies or local storage, changing first-visit behavior. Use a new browser context for a clean run, or deliberately load the stored consent state when that is the intended scenario.
Full-page image differs from the visible viewport Full-page capture changes the captured area and may expose lazy-loaded content or layout changes. Choose viewport capture when the visible fold is the target; choose full-page when the whole document is required, then verify the resulting dimensions and content.

7. Performance, reliability, and cost

Browser automation requires launching or maintaining a browser and waiting for the relevant page state. For repeated captures, reuse a browser process where appropriate, but create isolated contexts when consent state must not leak between jobs. Set navigation and selector timeouts, handle failures, and always close resources. Avoid waiting for network idle as the only condition on pages with analytics, polling, or other continuous requests.

The DIY approach has no per-screenshot API charge, but it uses compute and requires browser installation, selector maintenance, and handling each site’s consent behavior. If you capture pages frequently or from a service without managing browser infrastructure, a screenshot API can reduce setup work. Compare behavior and billing rules carefully; do not assume all services treat failed pages or consent overlays alike.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request can return a PNG, JPEG, WebP, or PDF. For a page where cookie banners or other supported overlays obstruct the capture, it accepts the consent banner like a visitor and removes 60+ known consent platforms, newsletter popups, and chat widgets before the shot; each step can be turned off. It reports page verdict and billing status in response headers, and bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing. Its MCP tools let AI agents use take_screenshot, get_page_info, and capture_pdf.

Here is the one-call cURL example. Replace the URL with the page you need. See the ScreenshotNeo API docs for the request options and response details.

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}`);

ScreenshotNeo offers 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000. Every feature is on every plan. You can also tune capture behavior with options including viewport and device presets, full-page or element capture, wait conditions, custom CSS or JavaScript, headers and cookies, and caching.

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

FAQ

Should I accept or reject cookies before taking the screenshot?

Use the consent choice that represents the state you need to document. The selector should identify that exact action rather than assuming acceptance is always appropriate.

Can I use one selector for every website?

No. Consent platforms, labels, markup, and iframe usage vary. Find and wait for the control on the target site.

No. A banner is generally page content; a native JavaScript dialog is handled through the browser automation framework’s dialog API.

Should I capture the page or an element?

Capture the page when you need the document or viewport. Capture an element when the output should contain only a specific component.