Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Playwright Codegen records your browser actions; it does not automatically add screenshot steps. Start Codegen, perform the workflow you want to preserve, copy the generated test, then insert page.screenshot() or locator.screenshot() at the exact state you need. Use fullPage: true for an entire scrollable page, fixed emulation settings and disabled animations for repeatable captures, and masking for dynamic or private content.

What Codegen does—and where screenshots fit

Codegen opens a browser and Playwright Inspector, watches your interactions, and generates test actions. The generated script describes navigation, clicks, fills and assertions. Screenshot capture is normally a refinement you add after copying that script into your project.

Install Playwright Test in a Node.js project, then start recording:

npx playwright codegen https://example.com

The URL is optional; you can open a page manually in the Codegen browser. The command accepts options for browser selection, output-file selection and language targets, including Python. For a repeatable layout, set a viewport while recording:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
npx playwright codegen --viewport-size="800,600" https://example.com

Interact with the site until it reaches the state you want. Stop recording, copy the generated test from the Inspector, and place screenshot calls after the relevant navigation or interaction. Keep authentication state local: --save-storage=auth.json records it and --load-storage=auth.json replays it, but the file can contain credentials and cookies.

Complete generated-test example

This test captures the viewport, the complete page, and one component after the page has loaded:

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

test('capture page states', async ({ page }) => {
  await page.goto('https://example.com');
  await page.screenshot({ path: 'artifacts/viewport.png' });
  await page.screenshot({ path: 'artifacts/full-page.png', fullPage: true });
  await page.getByRole('banner').screenshot({
    path: 'artifacts/banner.png',
    animations: 'disabled'
  });
});

Create the artifacts directory before running if your project does not create it automatically. A supplied path writes the image to disk. Without path, the method returns an image buffer that you can send to a comparison or processing system.

Choose the screenshot scope

Need Call Result
Visible browser viewport page.screenshot({ path }) Only the currently visible area
Entire scrollable document page.screenshot({ path, fullPage: true }) A potentially very tall image including content below the fold
One component locator.screenshot({ path }) A clip around the matched element
Downstream processing const buffer = await page.screenshot() In-memory PNG, JPEG or WebP data

Viewport capture

Use the page method without fullPage when the screenshot represents what a user sees at a particular scroll position. Set the viewport before navigation or through Codegen’s --viewport-size option so responsive breakpoints do not change between runs.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Full-page capture

fullPage: true captures the complete scrollable page. Long documents create large files and can expose lazy-loading problems. Navigate to the page, wait for the content that matters, and verify that images loaded before capturing.

Element capture

Use a stable locator rather than a brittle generated CSS path:

const card = page.getByRole('article').filter({ hasText: 'Release notes' });
await card.screenshot({ path: 'artifacts/release-card.webp', animations: 'disabled' });

Locator screenshots wait for actionability and scroll the target into view. If more than one element matches, narrow the locator so the intended component is unambiguous.

Make Codegen screenshots reproducible

Fix rendering inputs

Choose a named device when mobile rendering is the requirement:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
npx playwright codegen --device="iPhone 13" https://example.com

Also control --color-scheme, --timezone, --geolocation and --lang when the site changes content or styling for those inputs. Use the same browser, viewport, device scale and locale in CI as on a developer machine.

Stop motion and hide unstable data

Animations can produce different pixels at the same checkpoint. Disable them for a locator or page capture where supported, and mask timestamps, rotating ads, avatars or account data:

await page.screenshot({
  path: 'artifacts/dashboard.png',
  fullPage: true,
  animations: 'disabled',
  mask: [page.locator('[data-testid="last-updated"]'), page.locator('.avatar')],
  scale: 'css'
});

scale: 'css' keeps output dimensions tied to CSS pixels. Use omitBackground: true when a transparent image is required. Mask sensitive regions before storing artifacts or sending them to a pixel-diff service.

Wait for the real state

Do not rely on an arbitrary short delay when a semantic signal exists. Add a locator assertion or wait for the selector that marks readiness. For pages that fetch data after navigation, wait until the relevant content is visible, then capture. This avoids screenshots of loading skeletons that happen to finish at different times.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Python Codegen and screenshots

Codegen can generate Python targets. A generated Python test can use the synchronous or asynchronous Playwright API; this synchronous example is directly runnable after installing Playwright:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page(viewport={"width": 800, "height": 600})
    page.goto("https://example.com")
    page.screenshot(path="artifacts/viewport.png")
    page.screenshot(path="artifacts/full-page.png", full_page=True)
    page.get_by_role("banner").screenshot(
        path="artifacts/banner.png", animations="disabled"
    )
    browser.close()

Use a Python Codegen command when you want Python output, then add the screenshot calls to the copied script. Option names use Python’s underscore style, such as full_page and omit_background.

Buffers, formats and visual regression

Playwright writes PNG, JPEG and WebP when the filename extension and options select that format. For an external visual comparison, keep the bytes in memory:

const buffer = await page.screenshot({ type: 'png' });
// Pass buffer to your pixel-diff or artifact uploader.

A comparison system should use the same browser version, viewport, device scale, fonts, locale, timezone and data fixture on every run. Mask content that is intentionally variable rather than weakening every threshold. Full-page images may be too tall for a particular diff tool; compare a stable component or split the page into meaningful regions when necessary.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Or skip the browser setup

ScreenshotNeo provides a one-request website screenshot API and an MCP server for AI agents. It accepts consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups and chat widgets before capture; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing result.

Use the same request from a shell, Python or Node.js. See the ScreenshotNeo documentation for parameters 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
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}`);

Options include full-page and CSS-selector captures, dark mode, 12 device presets or custom viewports, retina scale, PDF paper and page controls, HTML/CSS rendering, custom JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, Authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API and an OpenAPI specification. Parameter names used by other screenshot APIs also work, which can simplify migration. Its MCP tools—take_screenshot, get_page_info and capture_pdf—work with Claude, Cursor and other MCP clients.

The Free plan includes 1,000 screenshots each month with no card. Paid plans are Starter $5 for 3,000, 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, and every feature is on every plan. Create a free ScreenshotNeo account to get started.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting Codegen captures

The screenshot is blank or shows a loading state

Cause: capture occurs before the page’s data or images arrive. Fix: wait for a meaningful selector, assertion or network-idle condition, and confirm the URL and authentication state.

The full-page image misses content

Cause: lazy content is loaded only after scrolling, or the page changes height during capture. Fix: scroll or trigger the component before fullPage: true, wait for images and capture after layout stabilizes.

The element locator fails

Cause: the generated selector is dynamic, matches multiple nodes or targets an iframe. Fix: use role, label, text or a test identifier; narrow duplicate matches; for an iframe, first select its frame and then locate the element inside it.

Visual diffs change on every run

Cause: animations, timestamps, random data, fonts, locale or viewport differences. Fix: disable animations, mask volatile regions, freeze test data, install consistent fonts and fix emulation settings.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Authentication works locally but not in CI

Cause: missing or stale storage state, environment variables or origin-specific cookies. Fix: create storage state in the same environment, keep it secret, load it explicitly and verify the account page before capture.

The image is too large

Cause: a full-page document or high device scale. Fix: capture a component, use scale: 'css', resize downstream, or divide the document into stable sections. Do not reduce fidelity until the comparison requirements are clear.

Practical checklist

  • Record the workflow with Codegen and copy the generated test.
  • Insert the screenshot at the state that matters, not merely after navigation.
  • Choose viewport, full-page or locator scope deliberately.
  • Fix viewport, device, locale, timezone, color scheme and authentication state.
  • Wait for meaningful content and disable animations.
  • Mask private or intentionally dynamic regions.
  • Store artifacts securely and use buffers when a diff pipeline needs them.

Frequently Asked Questions

Can Codegen save a screenshot while I am recording?

Codegen records browser actions; add screenshot calls to the copied generated test after recording.

Which call captures only one component?

Call screenshot() on a locator, such as page.getByRole('banner').screenshot({ path: 'banner.png' }).

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What is the difference between a file and a buffer?

A path writes the image to disk; omitting it returns image bytes for processing or visual comparison.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.