ScreenshotNeo

BlogHow-to

How to Take a Screenshot of a Page After It Finishes Loading in Chromium

Wait for Chromium’s navigation, network, or a page-specific element before capturing. Here are runnable Puppeteer and CLI methods, plus fixes for common timing problems.

By the ScreenshotNeo team4 October 20268 min read

Use Puppeteer to control Chromium, wait for a meaningful readiness signal, then capture. For many pages, start with page.goto(url, { waitUntil: 'networkidle0' }). If the content you need is rendered asynchronously or lazy-loaded, also wait for a selector that identifies it. “Finished loading” is page-specific: a navigation event, a quiet network, and the appearance of required content are different signals.

This guide shows a complete Puppeteer workflow, a Chrome headless command-line option, ways to choose a wait condition, and fixes for common capture problems. Puppeteer is a browser automation library, and Chrome’s official overview lists screenshots among its uses. See the Puppeteer overview and consult the API reference for your installed version because supported options can evolve.

1. Define what “finished loading” means

A page can finish its initial navigation while JavaScript is still rendering content. It can also look visually complete while analytics or other background requests continue. Choose the condition that proves the screenshot will contain what you need:

Signal What it tells you Use it when Limitation
load The browser’s load event fired for navigation. The page relies on ordinary document and resource loading. It does not prove an application has finished rendering later content.
domcontentloaded The initial document was parsed. You need an early DOM and will wait for a specific element afterward. Images, app rendering, and other resources may still be in progress.
networkidle0 In the Chrome for Developers example, no network requests for 500 ms. The page settles its requests and a quiet network is a useful signal. Analytics, polling, streaming, or long-lived requests may prevent idleness. Network quiet does not itself prove the target content is visible.
Required selector A chosen DOM element appeared. A particular heading, chart, report, or content area must be present. The element can exist before it is fully populated or visually ready; select a condition that reflects the needed state.
Fixed delay / CLI timeout A set amount of time elapsed or a maximum wait was reached. A simple one-off capture needs a bounded wait. Elapsed time is not proof of readiness. Chrome’s CLI timeout can capture while loading continues.

The Chrome for Developers server-side rendering example uses networkidle0 and then page.waitForSelector('#posts'); it also notes that lazy-loaded pages may need more waiting. Treat network idleness as a practical signal, not a universal definition of completion. See Headless Chrome: an answer to server-side rendering JavaScript sites.

2. Capture with Puppeteer

Install Puppeteer in a Node.js project, save the following as screenshot.js, and run it with a URL argument. The selector is intentionally explicit: replace #content-that-must-appear with an element that proves the required content exists on your target page.

npm install puppeteer

# Save as screenshot.js
const puppeteer = require('puppeteer');

async function main() {
  const url = process.argv[2] || 'https://example.com';
  const browser = await puppeteer.launch({ headless: true });

  try {
    const page = await browser.newPage();
    await page.setViewport({ width: 1365, height: 900 });

    const response = await page.goto(url, {
      waitUntil: 'networkidle0',
      timeout: 60_000,
    });

    if (response && !response.ok()) {
      throw new Error(`Navigation returned HTTP ${response.status()}`);
    }

    await page.waitForSelector('#content-that-must-appear', {
      visible: true,
      timeout: 15_000,
    });

    await page.screenshot({ path: 'page.png', fullPage: true });
    console.log('Saved page.png');
  } finally {
    await browser.close();
  }
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Run it as node screenshot.js https://your-page.example. The fullPage option shown is Puppeteer’s page screenshot option; check the API reference for your installed version if its behavior or option support differs. Remove the selector wait only when network idleness alone is an adequate readiness condition for your page.

Pick a wait condition deliberately

Set waitUntil to the earliest navigation milestone that is useful for your page, then add a content-specific wait when the target is rendered after navigation. For example, domcontentloaded plus a visible selector can be suitable when the application fills a known container later. Use networkidle0 when network quiet is meaningful and the page does not keep background connections open.

A fixed sleep such as await new Promise(resolve => setTimeout(resolve, 2000)) is easy to add but brittle: it can waste time on fast pages and still be too short on slow ones. Prefer a selector or application-specific readiness condition when you can identify one. This is a reliability recommendation based on the documented behaviors of network-idle, selector waits, and timeouts; it is not a measured speed comparison.

Wait for content that loads on scroll

Some pages only request images or sections when they approach the viewport. A selector wait for the main container may complete while lower content remains absent. If the screenshot needs the entire long page, identify how that site triggers lazy loading and scroll through the page before capture, then wait for the expected content. A generic delay cannot guarantee that every lazy-loaded section has arrived. Validate the resulting capture against the content requirement.

3. Use Chrome’s headless command line for a one-off capture

When you do not need scripted selector checks, Chrome’s headless CLI can save a screenshot directly. The official command-line reference documents --screenshot, --window-size, and --timeout:

chrome --headless --no-sandbox \
  --window-size=1365,900 \
  --timeout=10000 \
  --screenshot=page.png \
  https://example.com

Use the executable name installed on your system if it differs from chrome. The --timeout value is a maximum wait in milliseconds before capture, even if the page is still loading; it is not a selector check or proof that content is ready. See the Chrome Headless command-line reference for current flags and usage.

The CLI is convenient for a single URL and fixed viewport. Puppeteer is a better fit when the capture must wait for a specific element, handle navigation failures, or run as a repeatable job with application-specific logic.

4. Configure the capture for the page you need

Viewport and full-page output

Set the viewport before navigation or capture so responsive layouts render at the intended size. Use a viewport that matches the target device or report layout. Full-page capture can include content beyond the initial viewport, but it does not automatically cause every lazy-loaded element to load. Check the screenshot option against your Puppeteer version.

Element readiness

Wait for an element that exists only when the required content is ready. If an element appears early and is populated later, wait for a more specific state—for example, a child result, a status attribute, or a site-specific completion marker. Avoid selectors that match a loading shell when the screenshot needs final data.

Timeouts

Use bounded timeouts so a stalled page does not leave a capture worker waiting forever. The example allows 60 seconds for navigation and 15 seconds for the required selector. Adjust these limits to the page and job constraints; they are example values, not universal performance targets. When a wait expires, report which condition failed so the cause is diagnosable.

5. Troubleshoot common capture failures

Symptom Likely cause Fix
Navigation times out at networkidle0 The page continuously polls, streams data, or makes background requests. Use a less strict navigation milestone such as domcontentloaded, then wait for the specific content selector. Keep a bounded timeout.
Screenshot is missing data rendered by JavaScript The navigation event completed before the application finished rendering. Wait for a selector or application-specific state that proves the content is ready.
Screenshot omits lower-page images or sections Content is lazy-loaded and was never brought into the loading region. Scroll through the relevant page area, wait for expected elements, and then capture. Verify that the full-page result includes them.
Selector wait times out The selector is wrong, the element is inside a frame or shadow root, the page failed, or the expected content never appeared. Check the final URL and page state, confirm the selector in the rendered DOM, and account for frames or shadow DOM where applicable.
CLI capture is blank or incomplete The maximum timeout elapsed before the page was ready, or the page needs a content-specific wait. Increase the CLI timeout for a one-off attempt or use Puppeteer for a readiness check tied to page content.
Capture works locally but fails in a job The job environment may have different network access, browser dependencies, or resource limits. Log navigation status and the failed wait condition; ensure Chromium can start and reach the target from that environment.
Screenshot has the wrong layout The viewport was set too late or does not match the intended responsive breakpoint. Set the viewport before navigation and choose dimensions that correspond to the target layout.

6. Performance, reliability, and cost

Performance: waiting for network idleness can add delay when a page has many resources or persistent requests. A selector wait can finish as soon as the content you need is available, but only if that selector reflects real readiness. Avoid arbitrary long sleeps and avoid waiting for a global quiet state when background traffic never stops.

Reliability: make readiness explicit, bound every wait, and close the browser in a finally block as in the example. Record the target URL, navigation response, and which wait timed out. For pages with lazy loading, validate that scrolling and the final capture include required content. Do not interpret a CLI timeout as successful readiness.

Cost: Puppeteer and Chrome headless require a runtime capable of launching Chromium, and automated jobs consume that runtime’s compute and storage. The research sources do not establish universal resource requirements or a benchmark, so size the job for your own environment and capture workload rather than relying on an invented figure.

7. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. For this page, a basic WebP capture looks like this:

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. Cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.

8. FAQ

Does “network idle” mean every visible part of a page has finished rendering?

No. It indicates a period without network requests in the documented example. A page can still need client-side rendering or lazy-loaded content, so wait for the content that matters.

Should I use a fixed delay instead of a selector?

Use a fixed delay only when a rough bounded wait is sufficient. A selector tied to the required content gives a more page-specific readiness check.

Can Chrome’s CLI wait for a particular CSS selector before capturing?

The cited CLI flags provide a screenshot and a maximum timeout. For a selector-based readiness condition, use Puppeteer and wait for the element before calling the screenshot method.

Why does a full-page screenshot still miss images?

Full-page output describes capture extent; it does not guarantee that offscreen lazy-loaded resources were requested. Trigger the page’s lazy-loading behavior and wait for the required content before capture.