ScreenshotNeo

BlogHow-to

Why Does Selenium Capture a Screenshot Before the Web App Updates?

Selenium waits for document readiness, not every app update. Learn to wait for the right UI condition before capturing a screenshot.

By the ScreenshotNeo team4 October 20267 min read

Selenium can capture a screenshot before a web app updates because page navigation readiness and application readiness are different states. A navigation can finish while JavaScript is still fetching data, rendering a component, or changing the page. Selenium captures the browser’s current state when the screenshot command runs; it does not automatically wait for your application’s particular update.

The reliable fix is to wait for a condition that represents the state you want to see—such as updated text becoming visible—then take the screenshot. A fixed sleep may hide the race on one run and fail on another.

1. Why the screenshot is early

WebDriver’s page-load strategy governs how long a navigation command waits for a document readiness milestone. Selenium’s default normal strategy waits for the document to reach complete. That does not guarantee that the whole application has finished its asynchronous work. Single-page applications, for example, can fetch and render data after the initial document is ready. The eager strategy returns at interactive, while none does not wait for document readiness. These strategies affect navigation; they are not application-specific synchronization. See Selenium’s browser options documentation.

A typical race looks like this: the test triggers a navigation or interaction, WebDriver returns from that command, the app continues updating asynchronously, and the test captures the screenshot before the desired state appears. The test and browser are progressing independently, so timing differences can make the same test pass or fail.

Screenshot capture records the current browser state. The WebDriver API describes a best-effort capture area, which may be the page, window, current frame, or display depending on circumstances. Capture area is separate from waiting for the application to update. See Selenium’s WebDriver API.

2. Wait for the state the screenshot must show

Choose a signal that means the intended update has actually happened: a result is visible, text has changed, an old element has been replaced, or a title has reached its expected value. Selenium’s wait guidance and Expected Conditions document condition-based waits for visibility, text, staleness, and other states.

Python example

This runnable pattern assumes the page has an element with ID updated-result that becomes visible after the action. Replace the URL, selector, and trigger with your application’s actual values.

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait

options = webdriver.ChromeOptions()
driver = webdriver.Chrome(options=options)

try:
    driver.get("https://example.com")
    wait = WebDriverWait(driver, 10)

    # Trigger the change if this page requires an action, for example:
    # driver.find_element(By.ID, "load-results").click()

    wait.until(EC.visibility_of_element_located((By.ID, "updated-result")))
    driver.save_screenshot("after-update.png")
finally:
    driver.quit()

The explicit wait polls until its condition succeeds or its timeout expires. The screenshot is issued only after that condition succeeds. Selenium’s Python API is documented at selenium.dev.

Node.js example

Use the same principle in JavaScript: perform the action, wait for a meaningful condition, then capture. This example waits for a result element to become visible.

const { Builder, By, until } = require('selenium-webdriver');

(async function captureAfterUpdate() {
  const driver = await new Builder().forBrowser('chrome').build();
  try {
    await driver.get('https://example.com');
    const result = await driver.wait(
      until.elementLocated(By.id('updated-result')),
      10000,
      'Updated result did not appear'
    );
    await driver.wait(until.elementIsVisible(result), 10000);
    const image = await driver.takeScreenshot();
    require('fs').writeFileSync('after-update.png', image, 'base64');
  } finally {
    await driver.quit();
  }
})();

Install the Selenium JavaScript package and configure a compatible browser and driver in your environment before running the script. The selector and URL are placeholders for the page under test.

cURL and Python requests

cURL and Python requests can make HTTP requests, but they do not drive a browser or provide Selenium’s DOM-based expected conditions. They cannot replace an explicit wait inside a Selenium test when the requirement is to capture a browser after a specific application state. For an API-based screenshot of a public URL, ScreenshotNeo can handle capture separately; see the option below.

3. Select the right wait condition

What should be true Useful condition When it fits
A result was added to the page Element is present, then visible The page inserts a new result node.
A value changed Expected text is present or text matches a predicate The element exists before and after the update.
A refresh replaced the old result Old element becomes stale, then locate and wait for the replacement The framework replaces rather than edits the node.
A navigation reached the expected page Expected title or URL The desired screenshot follows a page navigation.

Element presence only tells you that a node is in the DOM. It does not necessarily mean it is visible or contains final content. Likewise, visibility alone may be an intermediate state if the application reveals a loading shell before filling in the result. Match the condition to the application’s real success signal.

4. Fixed sleeps, implicit waits, and page-load strategy

Approach What it waits for Limitation
Fixed sleep A predetermined duration May be too short on slow runs and wastes time on fast ones.
Implicit wait Element lookup availability, globally Does not establish that an existing element has finished updating visually.
Explicit wait A chosen condition at a specific point Requires a condition that truly represents the desired state.
Page-load strategy Document readiness during navigation Does not wait for all application-specific asynchronous work.

Prefer an explicit wait when the screenshot depends on a describable UI state. Selenium warns that mixing implicit and explicit waits can produce unpredictable total wait durations. Keep implicit wait at its default of zero when using explicit waits unless you have a deliberate reason to configure otherwise. A page-load strategy also does not govern a navigation caused by clicking an element or submitting a form in the same way it governs navigation by URL.

5. Handle replaced elements and delayed visual changes

When a framework replaces a result node, a previously located WebElement can become stale. Wait for the old reference to become stale, then locate the new node and wait for its final state. Do not keep querying an obsolete reference.

If a wait passes but the image still looks early, the condition may represent an intermediate state. For example, a container might become visible before its data arrives. Wait on the final text, a completion marker, or another application signal. Also check whether an animation or a later network update changes the page after the condition passes. The right signal depends on the app; WebDriver cannot infer which visual result your test considers complete.

6. Troubleshooting

Symptom Likely cause Fix
Screenshot shows old text Capture runs before the text update Wait for the expected updated text, not merely for the element to exist.
Wait succeeds, but screenshot shows a loading shell The condition is an intermediate milestone Choose a condition tied to completed content or the app’s final-state marker.
“Element not found” or wait timeout Wrong selector, wrong frame, or the action that reveals the element did not run Verify the selector in the live DOM, trigger the intended action, and switch to the correct frame if needed.
Stale element reference The page replaced the element after it was located Wait for staleness if relevant, then locate the replacement again.
Runs are slow or total wait time seems unexpected Long fixed sleeps or implicit and explicit waits combined Use condition-based explicit waits and avoid mixing wait types.
Issue varies by browser or driver Driver-specific behavior or environment differences First confirm synchronization and command order, then compare browser runs and inspect driver-specific behavior. Selenium notes that some issues originate in underlying drivers.

For more diagnostic context, consult Selenium’s troubleshooting assistance.

7. Performance, reliability, and cost

An explicit wait can return as soon as its condition becomes true, so it avoids the guaranteed delay of a long fixed sleep when the page is fast. Set a timeout long enough for expected application behavior, but keep it bounded so a missing update fails clearly rather than stalling indefinitely. A timeout is a useful failure signal: it tells you the state the screenshot depends on was not observed.

For reliable screenshots, make the condition specific, use stable selectors, keep the action and wait close together, and capture only after success. If the page has multiple updates, wait for the one relevant to the image. Do not assume a generic document-ready check, network quiet period, or arbitrary delay proves the application is visually settled.

In a Selenium workflow, the main cost of poor synchronization is engineering time and slower or flaky runs. A browser-based test remains appropriate when you need to exercise a user flow or capture an authenticated, stateful page. For a straightforward screenshot of a URL, a screenshot API can avoid maintaining browser setup; the request still needs to target a page whose state is suitable for capture.

8. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. For a public URL that does not require your Selenium interaction sequence, make one request:

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 and consent banners are accepted and removed before capture, along with known newsletter popups and chat widgets; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server lets AI agents use screenshot, page-info, and PDF tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up free for 1,000 screenshots a month with no card.

9. FAQ

Does document.readyState equal complete mean my app is finished?

No. It describes document readiness, not necessarily the completion of later JavaScript-driven application updates.

Should I wait for network idle before every screenshot?

Only if that state is a meaningful signal for your page. Prefer a condition tied to the content the screenshot must show.

Can Selenium know what “finished” means for my app?

No. The test must define the relevant success condition, such as expected text or a visible result.

Why does the screenshot API example not reproduce my Selenium flow?

A URL capture does not perform the page-specific interactions in your test. Keep Selenium when the screenshot depends on those interactions or browser state.