Recommended Free Tools
Because page.screenshot() captures when your test reaches that call; it does not wait for your app’s content to finish rendering. By default, page.goto() waits for the browser’s load event, but that milestone does not guarantee that data, hydration, or other application-specific changes are complete. Wait for the exact visible state your screenshot needs, then capture it.
What Playwright waits for—and what it does not
The screenshot API takes the page as it is when the awaited test flow reaches await page.screenshot(). It does not independently check whether a heading, data panel, image, or other application content is ready. Playwright’s documented example navigates and then captures; readiness beyond navigation is something your test must define. See the Page API documentation.
By default, page.goto(url) waits for the load event. That is a browser lifecycle milestone, not a promise that every asynchronous application task has finished. A page can continue to fetch data, hydrate client-side content, or reveal delayed widgets after load.
Choose a wait that matches the state you need
| Condition | What it means | When it helps |
|---|---|---|
commit |
The response is received and document loading has started. | When you need to know navigation has begun, not that the document or app is ready. |
domcontentloaded |
The target frame fires DOMContentLoaded. |
When the parsed document is the relevant milestone. |
load |
The frame fires the load event; this is the default for page.goto(). |
When the browser’s load event is sufficient for the next action. |
networkidle |
No network connections for at least 500 ms. | Playwright discourages using this as a test-readiness signal; prefer an assertion on the page state you need. |
These options describe navigation or network conditions, not necessarily the application result your screenshot is meant to show. The Page API specifically advises against using networkidle for testing and recommends web assertions to assess readiness.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware match#1 Best Overall
Wait for the content that should appear in the screenshot
Use a web-first assertion for the expected state. Playwright retries these assertions until they pass or the assertion timeout is reached. For example:
import { test, expect } from '@playwright/test';
test('captures the ready dashboard', async ({ page }) => {
await page.goto('https://example.com/dashboard');
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
await expect(page.getByTestId('report-status')).toHaveText('Ready');
await page.screenshot({ path: 'dashboard.png' });
});
Replace the URL, heading, and test ID with the values for your app. Assert the state that matters: expected text for a data panel, visibility for a required element, or an appropriate loaded/visible condition for an essential image. The web-first assertions guide explains the retrying assertions and includes toHaveScreenshot() among the page assertions.
Rank #2
If your goal is a visual comparison, you can use await expect(page).toHaveScreenshot() after the app reaches its intended state. A screenshot matcher does not replace the need to synchronize the application-specific state first.
Why common waits can still produce an early-looking screenshot
- The test waits only for navigation.
loadsays the browser fired its load event; it does not assert that the app’s data or interface has settled. - The test sets an early navigation condition. An explicit
waitUntil: 'commit'or'domcontentloaded'returns before the defaultloadmilestone. - A locator action succeeded. Locator actions wait for actionability conditions on their target. That does not establish that unrelated content elsewhere on the page is ready.
- The app renders content after load. Data fetching, hydration, delayed widgets, and user-triggered content can all create a gap between a lifecycle event and the state you want. Confirm which visible state is missing on the page.
- The test uses a fixed delay or network quietness as a proxy. A timeout may add unnecessary time without proving the required state; background requests can also make network quietness a poor fit. Prefer an assertion tied to the expected content.
- A later operation triggers navigation. Inspect navigation-triggering actions and any navigation wait, including the selected
waitUntilcondition.
Troubleshoot a screenshot that is still too early
- Identify what is missing. Name the specific text, element, image, or data state that should be present in the capture.
- Check the test sequence. Find the last navigation or action before the screenshot. Confirm it is awaited and review any explicit navigation condition.
- Add an assertion for the missing state. Use a locator and a web-first assertion, such as
toBeVisible()ortoHaveText(), before the screenshot. - Run the test again and inspect the assertion. If it times out, the assertion tells you the intended state was not reached within its timeout; investigate the selector, expected value, or app behavior rather than adding an arbitrary pause.
- Use a screenshot matcher only for visual comparison.
toHaveScreenshot()is useful when comparing rendered output, but first synchronize on the state the comparison should represent.
Without the test code, URL, app behavior, and capture sequence, there is no way to diagnose one particular screenshot with certainty. The general issue is a mismatch between the condition the test awaited and what you mean by “ready.”
Free tools Windows power users keep installed
One-click scans. No signup required.
Or skip the browser setup
If you need a website screenshot rather than a Playwright test, ScreenshotNeo offers a one-request screenshot API. It is not a fix for synchronization inside your Playwright test; it is an alternative when the task is simply to capture a URL.
Quick Recap
Rank #4
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for request options. Cookie banners are accepted and removed before capture, along with known newsletter popups and chat widgets. Bot checks, blank pages, and failed loads are not billed; response headers identify the page verdict and billing status. An MCP server lets AI agents use screenshot tools, and the free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.
Sign up for ScreenshotNeo’s free plan.
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.




