ScreenshotNeo

BlogHow-to

How to Wait for Network Idle Before a Website Screenshot in Playwright

Use Playwright’s `networkidle` load state before capture, but wait for the page condition that actually makes your screenshot ready.

By the ScreenshotNeo team4 October 20266 min read

To wait for network activity to quiet down before taking a screenshot, pass waitUntil: 'networkidle' to page.goto(), then call page.screenshot(). Playwright defines this state as having no network connections for at least 500 ms. For screenshot tests, however, network quiet does not prove that the content you need is ready; prefer an assertion for the expected page state.

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  await page.goto('https://example.com', { waitUntil: 'networkidle' });
  await page.screenshot({ path: 'screenshot.png', fullPage: true });

  await browser.close();
})();

The examples below use Playwright’s Node.js API. Install the package with npm install playwright, then install a browser with npx playwright install chromium. See the Playwright Page API for the documented navigation, load-state, and screenshot methods.

1. Choose the right wait

There are two ways to wait for network idle. Use the navigation option when the wait belongs to a new navigation. Use page.waitForLoadState() when navigation has already committed and you want to wait on the current document.

Situation Use
Opening a URL and waiting as part of navigation page.goto(url, { waitUntil: 'networkidle' })
Navigation has already committed page.waitForLoadState('networkidle')
Test needs a particular element or result Wait for or assert that page-specific condition
Visual regression test Assert the expected state, then use toHaveScreenshot()

Playwright discourages using network idle as a test readiness signal and recommends web assertions instead. A page may be quiet while still missing the expected content, or it may continue making background requests after the useful content has appeared.

2. Wait as part of navigation

This is the direct pattern for a one-off capture. The screenshot call runs only after the navigation promise resolves at the requested load state.

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  try {
    const page = await browser.newPage();
    await page.goto('https://example.com', { waitUntil: 'networkidle' });
    await page.screenshot({ path: 'screenshot.png' });
  } finally {
    await browser.close();
  }
})();

For a simple capture, this is usually the clearest option because navigation and the requested wait are one operation. The screenshot may be viewport-sized by default; set fullPage: true when you need the full scrollable page.

3. Wait after navigation has committed

If another operation has already navigated the page, wait on its current document:

await page.goto('https://example.com');
await page.waitForLoadState('networkidle');
await page.screenshot({ path: 'screenshot.png' });

waitForLoadState() requires navigation to have committed. It resolves immediately if that load state has already been reached, so calling it after the page has already gone quiet does not create a fresh 500 ms quiet period. Use the navigation form when you need the wait tied directly to opening the URL.

4. Wait for the content your screenshot needs

Network activity and visual readiness are different conditions. For a reliable capture, identify the element or state that makes the image useful and wait for that condition. For example, wait for a heading to become visible before capturing:

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  try {
    const page = await browser.newPage();
    await page.goto('https://example.com');
    await page.getByRole('heading', { name: 'Example Domain' }).waitFor();
    await page.screenshot({ path: 'screenshot.png' });
  } finally {
    await browser.close();
  }
})();

Choose a locator and condition that match the page you are capturing: a result row appearing, a loading indicator disappearing, or a chart becoming visible. A locator wait only establishes the condition you asked for; if the page updates the same element later, assert the final expected value or state as well. Avoid substituting an arbitrary delay when a meaningful page condition is available.

5. Use screenshot assertions for visual regression tests

In Playwright Test, first assert that the expected page state has been reached, then compare a screenshot:

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

test('example page screenshot', async ({ page }) => {
  await page.goto('https://example.com');
  await expect(page.getByRole('heading', { name: 'Example Domain' })).toBeVisible();
  await expect(page).toHaveScreenshot('example.png');
});

Playwright Test’s toHaveScreenshot() waits until two consecutive screenshots match before comparing the result with the expectation. This helps stabilize visual comparison, but it does not replace asserting that the intended content or application state is present. See the official visual comparisons documentation.

6. Configure the capture deliberately

The core network-idle choice is the same whether you save a viewport or full-page image. Set the screenshot options for the artifact you need:

Need Example
Full scrollable document page.screenshot({ path: 'page.png', fullPage: true })
Specific viewport await page.setViewportSize({ width: 1440, height: 1000 })
Hide animations for a test capture page.screenshot({ path: 'page.png', animations: 'disabled' })
Capture a particular element await page.locator('main').screenshot({ path: 'main.png' })

These are screenshot settings, separate from the load-state wait. A full-page capture can cause lazy-loaded content to load as the page is scrolled; if that content matters, make sure it has rendered before relying on the image. For selectors and supported screenshot options, consult the Page API.

7. Troubleshoot network-idle captures

Symptom Likely cause What to do
goto() times out waiting for network idle The page keeps connections or requests active, such as polling or streaming. Wait for navigation with an earlier load state, then wait for a specific element or application condition. Do not make network idle a requirement if the page never becomes quiet.
The screenshot is blank or missing the expected result The network became quiet before the relevant content appeared, or the capture reached the wrong page state. Assert the expected heading, result, or component before taking the screenshot. Check that navigation completed at the intended URL.
waitForLoadState() does not wait another 500 ms The requested state may already have been reached. That is documented behavior. Put the wait in goto() if it should be tied to navigation; otherwise wait for the application condition you actually need.
Screenshot changes between runs Dynamic content, animations, dates, or external data can vary even after the network quiets. Assert the intended state, control varying inputs where possible, and use Playwright Test’s screenshot assertion for visual stabilization.
Full-page image omits content lower down Lazy-loaded elements may not have rendered before capture. Wait for the relevant content and verify it is present before taking a full-page screenshot.

8. Performance, reliability, and cost

Network idle adds a minimum quiet period of 500 ms after the last observed network connection ends, and the total wait can be longer while requests continue. On pages with polling or persistent connections, it may never be the right completion condition. A page-specific wait can finish as soon as the needed content is ready, while also making the capture’s intent clearer.

For reliability, give navigation and page-condition waits appropriate timeouts for your environment, close the browser in a finally block, and make failure visible rather than saving a misleading image. Screenshot cost depends on where and how often you run browsers; Playwright itself does not charge per screenshot. Account for browser compute, memory, storage, and any hosted browser service you choose. No benchmark or fixed resource cost is implied here.

9. Or skip the browser setup

If you want an image without installing and managing a browser, ScreenshotNeo provides a website screenshot API and MCP server. One GET request accepts a URL and returns an image or PDF. See the ScreenshotNeo API documentation.

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

ScreenshotNeo accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Every feature is on every plan. Learn more at ScreenshotNeo, or sign up free for 1,000 screenshots a month with no card.

10. FAQ

Does network idle mean every image has finished rendering?

No. It describes network quiet, not whether a particular visual element has appeared or finished changing. Wait for the content that matters to the capture.

Should I use network idle in every screenshot test?

No. Playwright discourages it as a test readiness signal. Prefer web assertions for application state, and use screenshot assertions when you need a visual comparison.

Can I take a screenshot without waiting for network idle?

Yes. Navigate, wait for the specific page condition you need, and call page.screenshot(). For a regression test, use toHaveScreenshot() after asserting the expected state.