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

Wait for the UI state your screenshot needs—not an arbitrary number of milliseconds. In Playwright, use a locator wait or web-first assertion for expected text, visibility, or application status. Use toHaveScreenshot() for visual-regression tests because it waits for two consecutive identical captures before comparing them. Treat networkidle as a specialized navigation signal, not proof that a client-rendered page is ready.

The reliable pattern: assert the state, then capture

A screenshot can be taken after navigation has technically finished while the meaningful content is still being rendered. A result panel may exist but still be empty, a lazy image may not have loaded, or an animation may still be changing pixels. Make the prerequisite explicit in the test:

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

test('captures the results state', async ({ page }) => {
  await page.goto('https://example.com/search');
  await page.getByRole('button', { name: 'Search' }).click();

  await expect(page.getByRole('heading', { name: 'Results' })).toBeVisible();
  await expect(page.getByTestId('results-list')).toContainText('Product');

  await expect(page).toHaveScreenshot('results.png');
});

The locators and text must match your application. The important sequence is action, semantic assertion, then screenshot. Web-first assertions retry until their condition is met, so a short API response or React/Vue render delay does not create a race.

Playwright’s locators guide documents auto-waiting and assertions. A locator’s visible state means it has a non-empty bounding box and is not visibility:hidden; it does not guarantee that nested media, fonts, or animations have completed. Add assertions for those outcomes when they matter to the image.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Philips 24 Inch Computer Monitor FHD 100Hz VA VESA Flicker-Free, 241V8LB
  • CRISP CLARITY: This 23.8″ Philips V line monitor delivers crisp Full HD 1920x1080 visuals. Enjoy movies, shows and videos with remarkable detail
  • INCREDIBLE CONTRAST: The VA panel produces brighter whites and deeper blacks. You get true-to-life images and more gradients with 16.7 million colors
  • THE PERFECT VIEW: The 178/178 degree extra wide viewing angle prevents the shifting of colors when viewed from an offset angle, so you always get consistent colors
  • WORK SEAMLESSLY: This sleek monitor is virtually bezel-free on three sides, so the screen looks even bigger for the viewer. This minimalistic design also allows for seamless multi-monitor setups that enhance your workflow and boost productivity
  • A BETTER READING EXPERIENCE: For busy office workers, EasyRead mode provides a more paper-like experience for when viewing lengthy documents

Choose the wait that matches the screenshot dependency

Need Use What it establishes Important limitation
Element present, visible, hidden, or detached locator.waitFor({ state }) The locator reached the selected DOM or visibility state It does not prove application data or nested media is finished
Specific result or text expect(locator).toBeVisible(), toContainText(), or another web-first assertion The semantic condition is true, with retrying The assertion must express the actual screenshot prerequisite
Document navigation lifecycle page.waitForLoadState('domcontentloaded') or 'load' The selected browser lifecycle event occurred Usually unnecessary before actions and not an app-readiness contract
Visual regression comparison expect(page).toHaveScreenshot() or expect(locator).toHaveScreenshot() Two consecutive captures are identical before comparison Requires the Playwright Test runner
Image artifact only page.screenshot() or locator.screenshot() A file or buffer is produced No documented two-consecutive-capture assertion loop

These APIs and their current behavior are documented in the Locator API, PageAssertions API, and LocatorAssertions API. Check those pages against the Playwright version installed in your project; the documentation can evolve.

Waiting for a locator state

Use locator.waitFor() when the DOM state itself is the dependency:

const chart = page.getByTestId('sales-chart');
await chart.waitFor({ state: 'visible' });
await chart.screenshot({ path: 'sales-chart.png' });

Valid states are attached, detached, visible, and hidden. The default is visible. For a loading indicator, waiting for disappearance can be useful:

await expect(page.getByRole('progressbar')).toBeHidden();
await expect(page.getByTestId('dashboard')).toBeVisible();
await page.screenshot({ path: 'dashboard.png', fullPage: true });

Prefer locator-based waits over the older page.waitForSelector; the Page API marks that API as discouraged in favor of locators and web assertions. A selector’s presence alone is a weak readiness signal if the application fills it asynchronously.

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

Why networkidle is not a universal answer

page.waitForLoadState('networkidle') resolves after there have been no network connections for at least 500 ms. The Playwright Page API labels this state “DISCOURAGED” for testing and recommends web assertions instead. The same API says that waitForLoadState is “Most of the time” unnecessary because Playwright auto-waits before actions.

Rank #2
Philips 22 Inch Computer Monitor FHD 100Hz VA VESA Flicker-Free, 221V8LB
  • CRISP CLARITY: This 22 inch class (21.5″ viewable) Philips V line monitor delivers crisp Full HD 1920x1080 visuals. Enjoy movies, shows and videos with remarkable detail
  • 100HZ FAST REFRESH RATE: 100Hz brings your favorite movies and video games to life. Stream, binge, and play effortlessly
  • SMOOTH ACTION WITH ADAPTIVE-SYNC: Adaptive-Sync technology ensures fluid action sequences and rapid response time. Every frame will be rendered smoothly with crystal clarity and without stutter
  • INCREDIBLE CONTRAST: The VA panel produces brighter whites and deeper blacks. You get true-to-life images and more gradients with 16.7 million colors
  • THE PERFECT VIEW: The 178/178 degree extra wide viewing angle prevents the shifting of colors when viewed from an offset angle, so you always get consistent colors
await page.goto('https://example.com');
await page.waitForLoadState('domcontentloaded'); // only when this lifecycle point is needed
await expect(page.getByRole('heading', { name: 'Account' })).toBeVisible();
await page.screenshot({ path: 'account.png' });

Network quiet can occur before a framework commits its final render, while analytics, polling, or a websocket can keep a page from becoming quiet. If your product exposes a real readiness signal—such as “Report generated” or an enabled Export button—assert that signal instead. Use a load-state wait when you explicitly need a navigation event, not as a substitute for product state.

Capture files versus visual assertions

Use page.screenshot() for an artifact

await page.screenshot({
  path: 'full-page.png',
  fullPage: true,
  animations: 'disabled'
});

This writes an image (or returns a buffer if no path is supplied). It is appropriate for debugging, documentation, or sending a one-off capture to another system. A locator screenshot scrolls the element into view and performs actionability checks:

const invoice = page.getByTestId('invoice');
await expect(invoice).toBeVisible();
await invoice.screenshot({ path: 'invoice.png', animations: 'disabled' });

Those checks do not certify that asynchronous content inside the element is complete; assert the application state separately.

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

Use toHaveScreenshot() for regression tests

await expect(page).toHaveScreenshot('home.png');
await expect(page.getByTestId('invoice')).toHaveScreenshot('invoice.png');

According to the PageAssertions API, the assertion waits until two consecutive page screenshots yield the same result, then compares the last image with the expectation. Locator assertions provide the equivalent element workflow. These matchers are part of Playwright Test; they are not available when using only the lower-level browser library without the test runner.

Configure thresholds when your project needs them, but first remove avoidable sources of change: timestamps, randomized IDs, rotating ads, caret blinking, hover states, and animations. Mask or hide known dynamic regions rather than weakening every comparison.

Rank #3
Sale
Dell 24 Monitor - SE2426H - 23.8-inch FHD (1920x1080) 144Hz 1ms Display, in-Plane Switching (IPS) Technology, AMD FreeSync™, TÜV 3-Star 2X HDMI, Tilt
  • Clear visuals. Fluid motion: A 144Hz refresh rate and 1ms MPRT deliver smooth, tear‑free motion across work, gaming, and streaming for clearer, more fluid viewing.
  • Eye comfort: TÜV Rheinland 3‑star* certification reduces harmful blue light while preserving stunning color quality without compromise. *TÜV Rheinland 3-star eye comfort certification.
  • Wide viewing angle: Get consistent views across a wide 178° /178° viewing angle.
  • In-Plane Switching (IPS): See excellent color accuracy and consistency across wide viewing angles with In-plane Switching (IPS) technology.
  • Ultra-thin bezels: Maximize your viewing experience with thin bezels.

Make pixels stable before capture

Disable or complete animation

Screenshot assertions default to disabled animations. Finite animations are fast-forwarded to completion and transitionend is fired; infinite animations are canceled to their initial state and played again after capture. Direct locator screenshots document allow as their default, so set the option explicitly when motion must not affect an artifact:

await expect(page).toHaveScreenshot('checkout.png', {
  animations: 'disabled'
});
await page.getByTestId('checkout').screenshot({
  path: 'checkout.png',
  animations: 'disabled'
});

Control hover and pointer state

The visual comparisons guide recommends moving the pointer away from hover-sensitive elements, or hovering an element that has no visual hover effect. A deliberate pointer position prevents a tooltip or highlighted menu from appearing only in some runs:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.mouse.move(0, 0);
await expect(page).toHaveScreenshot('page.png');

If a test must capture a hover design, perform the hover immediately before the assertion and assert the resulting state.

Wait for lazy media and application data

“Visible” does not mean an image has decoded. Assert a meaningful image or status, or wait for a known application event:

await expect(page.getByRole('heading', { name: 'Profile' })).toBeVisible();
await expect(page.getByAltText('Profile photo')).toBeVisible();
await expect(page.getByTestId('save-status')).toHaveText('Saved');
await expect(page).toHaveScreenshot('profile.png');

For a page where the image element appears before its pixels are usable, expose a product-level “loaded” state (for example, a status element) and assert that state. Avoid arbitrary sleeps; they are slow when the page is fast and flaky when it is slow.

Rank #4
Samsung 27" Essential S3 (S36GD) Series FHD 1800R Curved Computer Monitor
  • CURVED FOR ENHANCED ENGAGEMENT: An immersive viewing experience with a curved monitor that wraps more closely around your field of vision; It creates a wider view, enhancing depth perception and minimizing peripheral distraction
  • SMOOTH PERFORMANCE FOR SEAMLESS CONTENT: Stay in the action when playing games, watching videos, or working on creative projects; The 100Hz refresh rate reduces lag and motion blur so you don't miss a thing in fast-paced moments¹
  • MORE GAMING POWER: Gain the edge with optimizable game settings; Color and image contrast can be adjusted to see scenes more vividly and spot enemies hiding in the dark; Game Mode adjusts any game to fill the screen so you can view every detail²
  • KEEP IT EASY ON THE EYES: Care for your eyes and stay comfortable, even during long sessions; Advanced eye comfort technology certified by TÜV reduces eye strain by minimizing blue light and reducing irritating screen flicker²
  • INCREASED VERSATILITY: Connect to more; Plug devices straight into your monitor for increased flexibility, making your computing environment even more convenient

How do I wait for a page to load before taking a screenshot?

  1. Navigate. Call page.goto() and let the navigation promise resolve.
  2. Identify the visual prerequisite. Choose the heading, result text, enabled control, loading completion, or other state that must appear in the image.
  3. Assert it with a locator. Use a web-first assertion such as toBeVisible(), toHaveText(), or toBeEnabled().
  4. Stabilize pixels. Set animations: 'disabled', control the mouse, and mask genuinely dynamic regions.
  5. Capture. Use page.screenshot() or locator.screenshot() for a file, and toHaveScreenshot() for a comparison.
import { test, expect } from '@playwright/test';

test('stable dashboard screenshot', async ({ page }) => {
  await page.goto('https://example.com/dashboard');
  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
  await expect(page.getByTestId('data-status')).toHaveText('Ready');
  await page.mouse.move(0, 0);
  await expect(page).toHaveScreenshot('dashboard.png', {
    fullPage: true,
    animations: 'disabled'
  });
});

Common failures and fixes

The screenshot is blank or missing content

  • Cause: capture occurs after navigation but before client rendering.
  • Fix: assert the expected heading, text, status, or result count before capture. Do not rely on a fixed timeout.

The test times out waiting for networkidle

  • Cause: polling, analytics, streaming, or another background connection prevents 500 ms of network silence.
  • Fix: remove the network-idle wait and assert the UI condition that proves readiness.

The element exists but the image is incomplete

  • Cause: locator visibility only establishes geometry and visibility; nested data or media is still changing.
  • Fix: add assertions for the relevant text, status, image, or completed state.

Visual snapshots fail intermittently

  • Cause: animation, hover, caret, time, random data, or responsive differences.
  • Fix: disable animations, move the pointer, freeze test data, mask dynamic regions, and keep browser/viewport settings consistent.

The locator screenshot throws because the element detached

  • Cause: the framework replaced the node during rendering.
  • Fix: wait for the stable application state and reacquire the locator; do not hold an element handle across a rerender.

toHaveScreenshot is unavailable

  • Cause: the test is using Playwright’s browser library without the Playwright Test runner.
  • Fix: run the test with @playwright/test, or use page.screenshot() for an artifact-only workflow.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability, and version considerations

Assertions return as soon as their condition is true, so they generally avoid the wasted time of a conservative sleep. Keep timeout values long enough for the slowest supported environment, but fix the readiness signal before increasing them. A navigation load event is useful when your next step depends on that lifecycle event; otherwise, Playwright actions and assertions already provide waiting behavior.

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

The API pages identify locator screenshots as available from Playwright 1.14, locator.waitFor from 1.16, and screenshot assertions from 1.23. These are method-introduction notes, not a recommended minimum version. Verify compatibility with your installed package and consult the stable documentation. The visual-comparison guide is under /docs/next/; confirm details against the stable page for your release.

Or skip the browser setup

If you need a clean screenshot service rather than maintaining browser-launch code, ScreenshotNeo accepts one GET request and returns PNG, JPEG, WebP, or PDF. It removes cookie/consent banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing result. It also provides an MCP server for Claude, Cursor, and other MCP clients, with take_screenshot, get_page_info, and capture_pdf tools.

See the ScreenshotNeo API documentation for all options, including waits, full-page and element capture, custom CSS/JavaScript, request blocking, cookies, headers, device presets, PDFs, caching, signed links, asynchronous jobs, bulk capture, and usage reporting.

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

The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is on every plan. Create a free ScreenshotNeo account to start.

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

FAQ

Should I use a fixed delay at all?

Only for a documented, unavoidable timing dependency. For application readiness, a condition-based assertion is faster and more reliable.

Best Value
Sale
Sceptre New 22-Inch Gaming Monitor, FHD 1080p, Up to 144Hz, HDMI, DisplayPort, Built-in Speakers, Machine Black (E225W-FW144 Series, 2026)
  • 【INTEGRATED SPEAKERS】Whether you're at work or in the midst of an intense gaming session, our built-in speakers provide rich and seamless audio, all while keeping your desk clutter-free.
  • 【EASY ON THE EYES】 Protect your eyes and enhance your comfort with Blue-Light Shift technology. This feature reduces harmful blue light emissions from your screen, helping to alleviate eye strain during long hours of use and promoting healthier viewing habits.
  • 【WIDEN YOUR PERSPECTIVE】Our sleek minimal bezel design ensures undivided attention. The nearly bezel-free display seamlessly connects in a dual monitor arrangement, delivering an unobstructed view that lets you focus on more at once, completely distraction-free.

Can I screenshot a single component instead of the whole page?

Yes. Use a locator’s screenshot or locator screenshot assertion after asserting the component’s own ready state.

Does a screenshot assertion wait for network idle?

No. It stabilizes consecutive screenshot output; your test still needs assertions that express the page state it requires.

Frequently Asked Questions

What timeout should I set for a screenshot wait?

Use the normal Playwright assertion timeout or a justified project-specific value, then fix the readiness condition before increasing it. A larger timeout cannot make an incorrect condition reliable.

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

How can I capture a page after a user-triggered download or export finishes?

Wait for the UI confirmation that the export completed, such as a success message or enabled preview, then capture that state. Do not infer completion solely from a quiet network.

Is page.waitForTimeout() a replacement for assertions?

No. It waits a fixed duration regardless of page speed and is prone to both wasted time and races. Prefer locator waits and web-first assertions tied to the intended screenshot.

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.