ScreenshotNeo

BlogHow-to

How to capture a screenshot before a cookie banner appears with Playwright

Capture early with Playwright’s commit or domcontentloaded milestone. Learn the tradeoffs, runnable code, and ways to handle banners that appear quickly.

By the ScreenshotNeo team4 October 20267 min read

To capture a page before a cookie banner appears, navigate with Playwright’s earliest useful checkpoint, then take the screenshot immediately. Try waitUntil: 'commit' for the earliest capture or waitUntil: 'domcontentloaded' when the page needs more time to build its initial DOM. Neither setting guarantees the banner will be absent: it may be included in the initial HTML or appear quickly in site JavaScript. Test the target page and choose the checkpoint that captures the content you need.

Choose the navigation checkpoint

Playwright offers several navigation milestones. For this task, the key decision is how much of the page to wait for before capturing:

Checkpoint What it means Tradeoff
commit The response has been received and document loading has started. Earliest documented checkpoint, but the capture can be blank or only partly rendered.
domcontentloaded The document’s DOMContentLoaded event has fired. More initial DOM may be present, but fast banner code has had longer to run.
load The page load event has fired. Usually gives scripts and resources more time, increasing the chance a banner appears.
networkidle There have been no network connections for at least 500 ms. Often too late for this goal. Playwright discourages using it as a general readiness condition.

Start with commit if capturing before the banner is the priority. If that image lacks the content you need, try domcontentloaded, then consider waiting for one specific content element. Each later checkpoint increases the race window. Playwright does not define when a site’s banner appears.

Runnable JavaScript example

Install Playwright and its Chromium browser in your project:

npm install playwright
npx playwright install chromium

Save this as screenshot.mjs. Set WAIT_UNTIL to commit or domcontentloaded to compare results on the target site.

import { chromium } from 'playwright';

const url = process.argv[2] ?? 'https://example.com';
const waitUntil = process.env.WAIT_UNTIL ?? 'commit';

if (!['commit', 'domcontentloaded'].includes(waitUntil)) {
  throw new Error('WAIT_UNTIL must be commit or domcontentloaded');
}

const browser = await chromium.launch({ headless: true });
try {
  const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
  await page.goto(url, { waitUntil, timeout: 30_000 });
  await page.screenshot({ path: 'before-banner.png' });
} finally {
  await browser.close();
}

Run and compare both checkpoints:

node screenshot.mjs https://example.com
WAIT_UNTIL=domcontentloaded node screenshot.mjs https://example.com

The example intentionally takes the screenshot as soon as goto() resolves. Avoid adding an arbitrary delay if the purpose is to beat a banner. If a particular heading or image must be present, a targeted readiness check may be necessary, but it gives the banner additional time to appear.

Use an init script only for specific early setup

page.addInitScript() runs after the document is created and before that document’s scripts run. Register it before navigation if you have site-specific setup that must run before page scripts:

await page.addInitScript(() => {
  // Add only target-specific setup that you have validated.
});
await page.goto(url, { waitUntil: 'commit' });
await page.screenshot({ path: 'before-banner.png' });

An init script does not automatically prevent a consent banner. Its effect depends on the code and site behavior, so verify the result on the actual target. For setup that should apply to pages and relevant frame navigations in a browser context, use browserContext.addInitScript(). If you register both context-level and page-level init scripts, do not depend on their relative execution order; Playwright documents that order as undefined.

Make the capture useful without waiting too long

  • Need the earliest possible page state: use commit, then inspect whether the response produced enough visible content.
  • Need parsed initial markup: try domcontentloaded and check whether the banner wins the race.
  • Need a particular element: wait for that selector only if it is essential. A readiness wait makes a banner more likely to appear before capture.
  • Need only to omit the banner from the image: page.screenshot() supports a style option that can apply CSS during capture. This affects the captured image; it does not prevent consent code from loading or establish what a visitor otherwise sees.
  • Page uses delayed or asynchronous UI: record which checkpoint and readiness condition produces an acceptable capture. There is no universally correct timing value.

Do not use networkidle by default. Playwright’s Page API documentation marks it discouraged and recommends web assertions to assess readiness instead. A page may also maintain connections that make network-idle waiting unsuitable.

cURL, Python, and Node.js with ScreenshotNeo

For a browser-managed alternative, ScreenshotNeo is a website screenshot API and MCP server for developers. Its one-call API returns an image or PDF. The capture service handles the browser setup; its documented feature set includes waits, custom CSS and JavaScript, selector capture, and controls for cookie banners and other overlays. See the ScreenshotNeo API documentation for request options.

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}`);
await import('node:fs/promises').then(({ writeFile }) =>
  writeFile('shot.webp', Buffer.from(await res.arrayBuffer()))
);

Replace YOUR_API_KEY with your key. Configure the desired output and capture behavior using the API options in the docs. ScreenshotNeo accepts the parameter names used by other screenshot APIs, which can make switching easier. Responses identify the page verdict and billing status in headers. Clean shots are billed; bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not.

Or skip the browser setup

Send one GET request to capture the page:

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

Cookie banners, newsletter popups, and chat widgets are removed before the shot, and each step can be turned off. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Sign up for 1,000 free screenshots a month.

Troubleshooting

Symptom Likely cause What to try
The capture is blank or nearly blank. commit captures just after the response arrives; rendering may not have produced visible content yet. Try domcontentloaded or wait for a specific essential element, then check whether the banner appears during that wait.
The cookie banner is still visible. The site included it in the initial HTML or its code displayed it before the selected checkpoint. Compare commit and domcontentloaded. If the goal is only to omit it visually, use screenshot-time CSS and verify the captured result.
The expected page content is missing. The screenshot was taken before that content rendered, or it is lazy-loaded. Wait for a content-specific selector if needed. This trades a higher chance of complete content for a longer opportunity for the banner to appear.
goto() times out. The page did not reach the chosen milestone within the timeout, or navigation stalled. Check the URL and connectivity, set a suitable explicit timeout, and handle navigation errors in the calling script. Avoid switching to networkidle as a blind workaround.
The banner appears in an iframe. Page-level observations or styles may not cover the frame as expected. Inspect the frame and its timing on the target. A context init script covers relevant frame attachments and navigations, but does not by itself suppress the banner.
Different runs capture different states. Page scripts, network timing, or banner display conditions vary between runs. Keep viewport, browser version, checkpoint, and readiness condition consistent; capture several manual comparisons while tuning the site-specific approach.

Performance, reliability, and cost

Earlier checkpoints generally reduce waiting, but may capture incomplete content. Later waits can improve completeness while allowing more scripts and overlays to appear. For repeatable captures, keep the viewport and browser configuration stable, use an explicit navigation timeout, close the browser in a finally block, and wait only for content the screenshot actually needs. Browser-based capture consumes compute while Chromium runs; keep the browser process reusable for batches when appropriate, and close pages and contexts when finished.

Playwright is software you run and operate, so account for browser installation, compute, and maintenance in your own environment. ScreenshotNeo’s published plans are Free: 1,000 shots/month; 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 on every plan. Only clean shots are billed; failed loads, cache hits, bot checks or CAPTCHAs, and blank pages cost nothing.

FAQ

Does commit guarantee the banner has not appeared?

No. It is the earliest documented checkpoint, but a site can include the banner in the initial response or show it immediately.

Should I use a fixed delay after navigation?

Not as a default. A delay gives the banner more time to appear. Use a specific readiness condition only when the screenshot needs the corresponding content.

Not by itself. It gives target-specific code an early execution point; any behavior change must be designed for and checked against the particular site.

Does screenshot CSS change the page for visitors?

No. The screenshot style option applies CSS during capture; it is not evidence that the page’s consent code was prevented or that the visitor’s page state changed.