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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

In Playwright, usually await the action that starts navigation, then assert the destination or the visible page state your test needs. Playwright waits for navigation triggered by actions automatically; add an explicit load-state wait only when that milestone matters. For most readiness checks, a retrying locator assertion is more reliable than a fixed sleep or waiting for all network activity to stop.

Choose the condition that proves the page is ready

“Page loaded” can mean several different things: a response arrived, the document was parsed, the browser fired its load event, or the interface became usable. Pick the condition that matches what the test will do next. A page can fire load before its app has rendered the data your test needs, and a page can remain busy with background requests after the relevant interface is ready.

Wait condition What it means When it fits
commit The response was received and document loading started. When the test needs to know navigation began and does not require parsed markup or rendered content.
domcontentloaded The document’s DOM was parsed; dependent resources such as images may still be loading. When the test can proceed from the parsed page structure.
load The browser fired the document’s load event. When resources whose loading contributes to that event are needed before proceeding.
networkidle No network connections for at least 500 ms. Rarely useful as a test-readiness condition; Playwright discourages using it for testing.

These milestones are documented in the Playwright Page API and navigation guide. “Network idle” does not mean the page is correct or usable. Analytics, polling, streaming, and other background activity can keep connections open; conversely, a quiet network does not prove a particular element has appeared.

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

Wait for navigation and assert the destination

When a click triggers navigation, await the click, then check the URL and a meaningful element. This keeps the test tied to user-visible outcomes instead of an estimated delay:

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

test('opens reports', async ({ page }) => {
  await page.goto('https://example.com');
  await page.getByRole('link', { name: 'Reports' }).click();

  await expect(page).toHaveURL(/reports/);
  await expect(page.getByRole('heading', { name: 'Reports' })).toBeVisible();
});

Playwright’s navigation-triggering actions wait for navigation as appropriate, and actions auto-wait for their actionability conditions. An explicit waitForLoadState() is often unnecessary; the Page API says, “Most of the time, this method is not needed because Playwright auto-waits before every action.” See the Page API and navigation guide.

For a single-page application that changes route without a document navigation, the URL and destination UI assertions are still useful: they wait for the expected result without incorrectly requiring a browser load event.

Use an explicit load-state checkpoint when it matters

For direct navigation, specify the milestone in page.goto() when you have a reason to stop at that point. The default navigation condition is load; choosing domcontentloaded can let a test proceed earlier if it only needs the parsed document.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import { test, expect } from '@playwright/test';

test('checks a page after document parsing', async ({ page }) => {
  await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
  await page.waitForLoadState('load');
  await expect(page.getByRole('main')).toBeVisible();
});

This example deliberately waits for both milestones. In real tests, remove the second wait if the test does not need the load event; do not add it by habit. You can also use waitUntil: 'commit' or waitUntil: 'networkidle', but the latter is discouraged as a testing readiness signal. For an assertion about app readiness, prefer the element, text, URL, or response condition that represents readiness.

Wait for the UI instead of sleeping

Replace arbitrary delays such as await page.waitForTimeout(3000) with web-first assertions. These retry until the condition passes or the assertion timeout expires:

await expect(page.getByTestId('results')).toBeVisible({ timeout: 10_000 });
await expect(page.getByRole('status')).toHaveText('Ready', { timeout: 10_000 });

Choose an assertion that reflects the test’s goal: visibility, text, URL, enabled state, or another observable condition. A fixed sleep wastes time when the page is fast and still fails when it is slower than the guessed duration. Playwright documents page.waitForSelector() as discouraged in favor of locator-based waiting and assertions; see the Page API.

If the application has a specific loading indicator, you can assert that it disappears or that the final content appears. Prefer the final state when possible: it makes the failure explain what the user actually needed rather than merely reporting that a spinner changed.

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.

Handle popups and secondary pages

Register the popup wait before clicking the control that opens it, so the event cannot be missed. Once the popup exists, wait for an appropriate milestone and assert its content:

const popupPromise = page.waitForEvent('popup');
await page.getByRole('button', { name: 'Open report' }).click();
const popup = await popupPromise;

await popup.waitForLoadState('domcontentloaded');
await expect(popup).toHaveTitle(/Report/);

The popup pattern and waiting for its load state are documented in the Playwright Page API. If the popup is a client-rendered app, the title or a locator for its ready state may be a better signal than waiting for a later load milestone.

Tell navigation timeouts from assertion and test timeouts

A timeout message points to different scopes of failure. Raising the wrong timeout can hide the symptom without fixing the cause.

Timeout or error What it applies to What to inspect
Navigation timeout A navigation wait. The current documentation does not state one universal default in its timeout table. Target URL, redirects, server response, and the selected waitUntil milestone.
expect(...): Timeout A web-first assertion. Current Playwright Test documentation gives a 5,000 ms default assertion timeout. Whether the locator matches the intended element and whether the expected state or value is correct.
Timeout of 30000ms exceeded Playwright Test’s test timeout. Current documentation gives 30,000 ms for the test function and fixture setup/teardown scope it describes. The whole test and fixture path, not only the last locator.

The values above are current defaults in the Playwright Test timeout documentation; configure them if needed rather than assuming a navigation has the same default as a test or assertion. The navigation guide and Page API describe navigation-specific waits and settings: navigation guide and Page API.

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

Fix a Playwright timeout in a focused sequence

  1. Reproduce the smallest failing operation. Reduce the test to the navigation or assertion that fails, then read the call log to see which condition Playwright was waiting for.
  2. Check the destination and redirects. Confirm the URL is valid and the server responds as expected. The navigation guide notes that page.goto() follows a client-side redirect that occurs before load: navigation guide.
  3. Replace fixed delays with observable conditions. Assert the destination URL, a visible heading, ready status, or other state the user needs.
  4. Match the milestone to the test. Use domcontentloaded for parsed HTML or load when the load event’s resources matter. Avoid networkidle as a blanket fix.
  5. Adjust only the narrow timeout that is too short. Set an assertion timeout for a genuinely slow assertion or a navigation timeout for a slow navigation, instead of raising the whole test timeout by default.
  6. Collect diagnostics if it remains intermittent. Capture a trace, screenshot, and relevant response details in the test environment. These are practical debugging steps, not Playwright defaults.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Common timeout symptoms and fixes

  • Navigation never reaches load: check whether a resource or redirect is stalled and whether the test truly needs that milestone. If the parsed DOM is sufficient, use domcontentloaded; if readiness is defined by app content, assert that content.
  • networkidle never arrives: the page may keep connections open for polling, analytics, or other work. Do not wait for network quiet if the test only needs a specific UI state.
  • The click succeeds but the next assertion times out: verify the route, locator, expected text, and whether the app performs client-side navigation. Assert the resulting URL or page state instead of adding a generic load-state wait.
  • The test hits 30 seconds despite a small final action: examine setup, fixtures, earlier actions, and teardown, because the test timeout covers a broader scope than one assertion.
  • Increasing the test timeout changes nothing: the failing wait may be governed by a separate expect or navigation timeout. Configure the narrow scope shown by the error and investigate why the condition is not occurring.

Or skip the browser setup

If your goal is to capture a rendered page rather than test browser behavior, ScreenshotNeo offers a one-request website screenshot API. Its clean-shot flow accepts cookie or consent banners like a visitor and removes 60+ known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and each response identifies the page verdict and billing status in headers. It also has an MCP server with take_screenshot, get_page_info, and capture_pdf tools for AI agents.

curl -G "https://api.screenshotneo.com/v1/shot" 
  -d access_key=YOUR_API_KEY 
  --data-urlencode url=https://example.com 
  -o shot.webp

See the ScreenshotNeo API documentation for request options. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, no card required.

Frequently Asked Questions

Does Playwright automatically wait for navigation after a click?

Yes. For actions that initiate navigation, Playwright waits for navigation as appropriate. Await the action and assert the destination or resulting UI; add a load-state wait only when that milestone is needed.

Should I use networkidle to fix flaky tests?

Usually not. Playwright discourages networkidle for testing; assert the UI or other condition the test actually requires.

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

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.