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.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →#1 Best Overall
- 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.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWhy 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
- 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.
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
- 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:
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
- 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?
- Navigate. Call
page.goto()and let the navigation promise resolve. - Identify the visual prerequisite. Choose the heading, result text, enabled control, loading completion, or other state that must appear in the image.
- Assert it with a locator. Use a web-first assertion such as
toBeVisible(),toHaveText(), ortoBeEnabled(). - Stabilize pixels. Set
animations: 'disabled', control the mouse, and mask genuinely dynamic regions. - Capture. Use
page.screenshot()orlocator.screenshot()for a file, andtoHaveScreenshot()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 usepage.screenshot()for an artifact-only workflow.
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.
Recommended Free Tools
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.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitchesFAQ
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
- 【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.
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.
Quick Recap
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.

