ScreenshotNeo

BlogHow-to

Best Playwright Settings for Screenshots of Websites with Consent Management Platforms

Make Playwright screenshots of consent banners and saved consent states reproducible with explicit browser settings, isolated contexts, and page-specific waits.

By the ScreenshotNeo team4 October 20268 min read

For reliable Playwright screenshots of sites with consent management platforms (CMPs), make browser inputs explicit and treat consent state as part of the test scenario. Use a fresh isolated context to capture a first-visit banner; use a separate context seeded with that site’s actual saved consent data to capture a returning visitor or a chosen preference. Set the viewport, device scale, locale, timezone, color scheme, and reduced-motion preference deliberately, then wait for a page-specific readiness signal before taking the screenshot.

There is no universal CMP cookie name or consent-storage format. Validate the actual site’s behavior, and keep the browser version, operating environment, fonts, and test data fixed when comparing pixels. This guide includes a runnable Playwright Test baseline, first-visit and saved-state patterns, capture options, troubleshooting, and an API alternative. See the official Playwright screenshot guide, BrowserContext API, BrowserType API, emulation guide, and TestOptions API.

1. Configure a reproducible browser scenario

Start with a named scenario and specify every browser-visible setting that can affect the page. The values below are an example for a London English desktop capture; replace them with the audience and device conditions you actually need to test.

import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    browserName: 'chromium',
    viewport: { width: 1440, height: 900 },
    deviceScaleFactor: 1,
    locale: 'en-GB',
    timezoneId: 'Europe/London',
    colorScheme: 'light',
    reducedMotion: 'reduce',
    serviceWorkers: 'allow',
  },
});

Locale affects browser language, the Accept-Language request header, and formatting of dates and numbers. Timezone emulation affects the browser, not the test runner’s own timezone. Playwright supports light and dark color schemes and reduced-motion settings of reduce and no-preference. Service workers are allowed by default; block them only when that is the deliberate scenario, because it changes page behavior.

For pixel comparisons, also pin and record the Playwright package and browser version, operating system or container image, installed fonts, and test data. The emulation settings control specific browser inputs; they do not guarantee identical rendering across different environments.

Do not reuse a shared browser profile for first-visit and returning-visitor captures. A browser context is an isolated session with its own cookies and storage, making it a useful boundary for each consent scenario.

  1. First visit: create a new context without saved cookies or storage state. Navigate to the page, verify the expected banner is visible, then capture it.
  2. Returning visitor: use the consent state generated by the real site flow, or a saved storage-state file created for that site. Start a separate context with that state and verify the expected post-consent view.
  3. Preference selection: interact with the CMP’s real controls, save the selection, and capture the result. Keep distinct cases for each meaningful category selection you need to check.

Example Playwright Test for a fresh first-visit state (replace the URL and selector with the target site’s real values):

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

test('first visit shows the consent banner', async ({ browser }) => {
  const context = await browser.newContext({
    viewport: { width: 1440, height: 900 },
    deviceScaleFactor: 1,
    locale: 'en-GB',
    timezoneId: 'Europe/London',
    colorScheme: 'light',
    reducedMotion: 'reduce',
  });
  const page = await context.newPage();

  await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
  const banner = page.locator('#consent-banner');
  await expect(banner).toBeVisible();
  await page.screenshot({ path: 'first-visit.png' });

  await context.close();
});

For a returning-visitor case, first create the storage-state file by completing the site’s real consent flow in a controlled context. Then use browser.newContext({ storageState: 'consent-state.json', ... }) in a separate test and assert the expected banner or preference state. Treat that state file as site-specific test data: storage keys, cookies, iframe behavior, and consent semantics vary. A banner disappearing is not proof that the choice persisted. Playwright’s BrowserContext documentation covers cookies and storage state.

addInitScript runs after document creation but before page scripts, so it can establish a controlled JavaScript environment. It is not a generic substitute for the CMP’s actual consent flow: hard-coded storage assumptions can produce a state the site would never create for a visitor.

3. Choose the screenshot scope and output deliberately

Capture choice Use it for Considerations
Viewport The visible, above-the-fold state This is the default page screenshot scope. Fix viewport dimensions for comparisons.
Full page The entire scrollable document Use fullPage: true. It cannot be combined with an element target. Sticky layouts or content that changes during scrolling may produce a different result from a normal viewport capture.
Element A CMP banner, dialog, or focused component Capture the relevant locator when the component itself is the subject. Include surrounding page context only if it matters to the question.
Scale Consistent image dimensions scale: 'css' produces CSS-pixel output; scale: 'device' uses device pixel ratio. Choose consistently.
Format A downstream consumer or comparison tool with a format requirement Set PNG, JPEG, or WebP explicitly when format matters.

Examples:

// Viewport screenshot (default scope)
await page.screenshot({ path: 'viewport.png', type: 'png', scale: 'css' });

// Entire scrollable page
await page.screenshot({ path: 'full-page.png', fullPage: true, type: 'png' });

// A focused CMP element
await page.locator('#consent-banner').screenshot({ path: 'banner.png', type: 'png' });

Playwright documents viewport, element, and full-page captures, along with CSS and device scales, in its screenshot documentation.

4. Wait for the state you intend to capture

Navigation completing does not necessarily mean the CMP or page content is ready. Wait for a meaningful signal tied to the scenario: the CMP container becoming visible, a specific API response completing, a loading indicator disappearing, or relevant fonts and images being ready. The correct condition depends on the target site.

await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.locator('#consent-banner').waitFor({ state: 'visible' });
await page.screenshot({ path: 'banner.png' });

A fixed delay can be a last-mile workaround for a known external delay, but should not be the only readiness condition. Use reducedMotion: 'reduce' when that matches the scenario and the site honors the media feature. Avoid disabling JavaScript for CMP captures, since consent interfaces commonly depend on scripts. Likewise, block service workers only when deliberately testing that condition.

5. Vary the settings that can change a CMP screenshot

Dimension Useful scenarios Possible effect
Consent persistence Fresh context; saved consent; changed preference Whether the banner, preference center, or post-choice state appears
Locale and location Language/locale; timezone; geolocation when the site uses it Banner copy, dates, regional prompts, and possibly page content
Viewport and device Fixed desktop; named mobile profile; target viewport Breakpoints, wrapping, overlay coverage, and visible area
Color and motion Light/dark; reduced motion on/off Styles and motion-sensitive transition state
Capture scope and output Viewport/full page/element; CSS/device scale; image format Image dimensions, context, and comparison behavior

Playwright supports context emulation for locale, timezone, geolocation, permissions, color scheme, and device profiles. Whether a particular CMP responds to any of those inputs is site-specific; verify it instead of assuming it.

6. A repeatable capture workflow

  1. Pin the project and browser environment. Name each scenario clearly, such as first-visit-desktop-en-GB or accepted-consent-mobile-fr-FR.
  2. Create an isolated context for each consent state and apply the scenario’s explicit browser settings.
  3. Navigate to the target and wait for a condition that proves the desired page and CMP state is ready.
  4. Assert that the expected consent UI or saved state is present, then capture the appropriate viewport, element, or full page.
  5. When investigating nondeterministic output, save a Playwright trace or other diagnostics alongside the screenshot. Retain the exact configuration and browser version with each artifact.

7. Troubleshooting

Symptom Likely cause Fix
The banner is missing in a first-visit screenshot Cookies or storage leaked from a reused context, or the site has not finished loading its CMP Create a new context without saved state, wait for the actual banner selector, and assert visibility before capture.
The banner is missing with saved state The stored state is stale, incomplete, or not the state the site expects Recreate it through the site’s real consent flow; verify the post-choice state in a fresh context.
Banner copy or layout changes between runs Locale, viewport, timezone, geolocation, or responsive breakpoint differs Set those inputs explicitly and use the same scenario values for every run.
The screenshot captures a transition or skeleton Capture began before page-specific content or the CMP became ready Wait for a meaningful selector, response, or loading-state change. Add a fixed delay only for a known residual delay.
Full-page result differs from the viewport view Sticky elements or content changing during the full-page capture Check the page’s scroll behavior; use viewport capture if the question concerns the visible screen.
Images differ despite matching context settings Browser version, operating system, fonts, data, or other rendering inputs changed Pin and record those environment inputs along with Playwright configuration.
A scripted consent setup does not persist The CMP uses site-specific storage, cookies, or frame behavior Inspect the real site’s behavior and seed its actual saved state instead of assuming a universal key.

8. Performance, reliability, and cost

For repeatable screenshots, isolation and explicit readiness checks are reliability controls: they prevent one test’s consent state from silently changing another and reduce captures taken mid-load. Reusing a context may reduce setup work, but it also carries cookies and storage forward; use it only when persistence is part of the scenario. Full-page capture can involve more page content than a viewport capture, and device-scale output changes image dimensions. Choose the scope and scale that answer the test question.

Playwright is the do-it-yourself browser automation route: you manage browser installation, environment consistency, scenario state, and capture code. There is no universal runtime or cost figure for these choices in the cited documentation, so measure them in the environment and workload you actually run. Keep failures diagnosable by retaining scenario names, versions, settings, and traces for flaky cases.

9. Or skip the browser setup

If you need a screenshot without maintaining browser contexts and capture infrastructure, ScreenshotNeo is a website screenshot API and MCP server by Yorker Media. One GET request returns a PNG, JPEG, WebP, or PDF. Its clean-shot steps accept cookie/consent banners like a visitor and remove 60+ known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Only clean shots are billed: bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the page verdict and billing status in headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

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,
)
r.raise_for_status()
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 Bun.write('shot.webp', res);

ScreenshotNeo’s free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card required.

10. FAQ

Use a fresh browser context with no saved cookies or storage, navigate to the page, and wait for and assert the site’s real banner element before capturing.

How do I take a full-page screenshot in Playwright?

Call page.screenshot({ fullPage: true }). Full-page capture cannot be combined with an element target.

Should I use a fixed sleep before taking the screenshot?

Prefer a page-specific readiness signal. A fixed delay can supplement it when a known external delay remains, but should not be the only condition.

No. Consent storage and semantics vary by site and vendor; derive saved state from the target site’s actual flow.