Skip to main content
SeleniumDecoded

Page Load Strategy and Timeouts

Control when driver.get() returns with normal, eager and none strategies, set the three session timeouts (page load, script, implicit), and combine them with explicit waits for fast, stable tests.

Selenium 4 Stable Updated 9 Sept 2026 · Verified against Selenium 4.48.0

Every navigation blocks until the browser says the page has loaded. What “loaded” means is configurable, and picking the right definition is the difference between a suite that waits 30 seconds for a third-party analytics script and one that moves on the moment your app’s HTML is parsed.

The Three Page Load Strategies

Strategyget() returns whenTypical use
normal (default)The load event fires: HTML plus all subresources (images, CSS, scripts, iframes)Traditional server-rendered pages
eagerDOMContentLoaded fires: HTML parsed, DOM built; subresources may still be loadingSingle-page apps and pages with slow third-party assets
noneImmediately after the navigation startsFull control with explicit waits; performance-sensitive suites

With eager or none you must wait explicitly for the element you need. That is good practice anyway.

Setting the page load strategy
Selenium 4 Stable
import org.openqa.selenium.PageLoadStrategy;
ChromeOptions options = new ChromeOptions();
options.setPageLoadStrategy(PageLoadStrategy.EAGER); // NORMAL, EAGER, NONE
WebDriver driver = new ChromeDriver(options);
driver.get("https://app.example.com"); // returns at DOMContentLoaded
new WebDriverWait(driver, Duration.ofSeconds(10))
.until(ExpectedConditions.visibilityOfElementLocated(By.id("app-ready")));
options = webdriver.ChromeOptions()
options.page_load_strategy = "eager" # "normal", "eager", "none"
driver = webdriver.Chrome(options=options)
driver.get("https://app.example.com")
WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, "app-ready")))
const chrome = require('selenium-webdriver/chrome');
const options = new chrome.Options().setPageLoadStrategy('eager');
const driver = await new Builder().forBrowser('chrome').setChromeOptions(options).build();
await driver.get('https://app.example.com');
await driver.wait(until.elementIsVisible(driver.findElement(By.id('app-ready'))), 10000);
var options = new ChromeOptions { PageLoadStrategy = PageLoadStrategy.Eager };
var driver = new ChromeDriver(options);
driver.Navigate().GoToUrl("https://app.example.com");
new WebDriverWait(driver, TimeSpan.FromSeconds(10))
.Until(d => d.FindElement(By.Id("app-ready")).Displayed);

The same option works on FirefoxOptions, EdgeOptions and SafariOptions; it is a W3C capability (pageLoadStrategy), not a Chrome extension.

When Each Strategy Makes Sense

Stick with normal for pages whose content depends on images or scripts finishing (visual tests, PDF generation) and when you are not sure. It is the safest default.

Choose eager for single-page apps. The framework’s JavaScript is usually loaded by DOMContentLoaded; everything after that (API calls, lazy images) needs an explicit wait regardless of strategy. Suites with heavy third-party assets (chat widgets, analytics, fonts) often cut minutes off their run time with this one change.

Choose none when you want to interact with a page while it is still loading, or when a page never fires load (some long-polling dashboards). Every interaction then needs an explicit wait, and your wait timeouts become the effective page-load timeout.

The Three Session Timeouts

Independently of the strategy, every session has three timeouts:

TimeoutDefaultApplies to
Page load300 sget(), navigate().to(), back(), forward(), refresh()
Script30 sexecuteAsyncScript()
Implicit0 sEvery findElement / findElements retry window

A page load that exceeds the timeout raises TimeoutException; the navigation is aborted. Set it lower than the default: a page that takes five minutes is a failed test, not a slow one.

Session timeouts at runtime and via options
Selenium 4 Stable
// At runtime
driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(30));
driver.manage().timeouts().scriptTimeout(Duration.ofSeconds(15));
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(0)); // keep it at 0
// Or in options so every session starts configured
ChromeOptions options = new ChromeOptions();
options.setPageLoadTimeout(Duration.ofSeconds(30));
options.setScriptTimeout(Duration.ofSeconds(15));
options.setImplicitWaitTimeout(Duration.ofSeconds(0));
// Read back what the driver has
System.out.println(driver.manage().timeouts().getPageLoadTimeout());
# At runtime
driver.set_page_load_timeout(30)
driver.set_script_timeout(15)
driver.implicitly_wait(0)
# Or in options (milliseconds)
options = webdriver.ChromeOptions()
options.timeouts = {"pageLoad": 30_000, "script": 15_000, "implicit": 0}
# Read back
print(driver.timeouts.page_load)
// At runtime (milliseconds)
await driver.manage().setTimeouts({ pageLoad: 30000, script: 15000, implicit: 0 });
// Read back
const timeouts = await driver.manage().getTimeouts();
console.log(timeouts.pageLoad);
// At runtime
driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(30);
driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(15);
driver.Manage().Timeouts().ImplicitWait = TimeSpan.Zero;

Handling a Page Load Timeout Gracefully

Sometimes the page is usable even though load never fires because one tracking pixel hangs. Catch the timeout, stop the load, and continue with explicit waits.

Recover from a slow load
Selenium 4 Medium
driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(15));
try {
driver.get("https://app.example.com/report");
} catch (TimeoutException e) {
((JavascriptExecutor) driver).executeScript("window.stop();"); // abort remaining requests
}
// Continue as long as what we need is present
new WebDriverWait(driver, Duration.ofSeconds(10))
.until(ExpectedConditions.presenceOfElementLocated(By.id("report-table")));
driver.set_page_load_timeout(15)
try:
driver.get("https://app.example.com/report")
except TimeoutException:
driver.execute_script("window.stop();")
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "report-table")))
await driver.manage().setTimeouts({ pageLoad: 15000 });
try {
await driver.get('https://app.example.com/report');
} catch (e) {
if (e.name !== 'TimeoutError') throw e;
await driver.executeScript('window.stop();');
}
await driver.wait(until.elementLocated(By.id('report-table')), 10000);
driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(15);
try
{
driver.Navigate().GoToUrl("https://app.example.com/report");
}
catch (WebDriverTimeoutException)
{
((IJavaScriptExecutor)driver).ExecuteScript("window.stop();");
}
new WebDriverWait(driver, TimeSpan.FromSeconds(10))
.Until(d => d.FindElements(By.Id("report-table")).Count > 0);

Waiting for “Ready” in a Single-Page App

document.readyState === 'complete' is the JavaScript equivalent of the load event and tells you nothing about pending API calls. Better signals, in order:

  1. A specific element that only renders after data arrives. This is the best signal in almost every case.
  2. An app-provided flag such as window.__APP_READY__ or a data-loaded attribute on the root; ask developers for one.
  3. Network idleness via BiDi: count network.beforeRequestSent and network.responseCompleted events and wait until they balance. See Custom Wait Conditions.
  4. Framework hooks: Angular exposes getAllAngularTestabilities(); jQuery apps expose jQuery.active === 0. Only for legacy apps.
Prefer the element, fall back to readyState
Selenium 4 Stable
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15));
// Best: the thing you need
wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("[data-testid='orders-table'] tbody tr")));
// Fallback: DOM finished parsing and subresources loaded
wait.until(d -> "complete".equals(((JavascriptExecutor) d).executeScript("return document.readyState")));
wait = WebDriverWait(driver, 15)
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "[data-testid='orders-table'] tbody tr")))
wait.until(lambda d: d.execute_script("return document.readyState") == "complete")
await driver.wait(until.elementLocated(By.css("[data-testid='orders-table'] tbody tr")), 15000);
await driver.wait(async () => (await driver.executeScript('return document.readyState')) === 'complete', 15000);
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(15));
wait.Until(d => d.FindElements(By.CssSelector("[data-testid='orders-table'] tbody tr")).Any(e => e.Displayed));
wait.Until(d => ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState").Equals("complete"));

For a modern web app under test, this combination is fast and predictable:

  • pageLoadStrategy = eager
  • pageLoadTimeout = 30 s (or lower, matching your performance budget)
  • implicitWait = 0
  • Explicit waits of 10 s on every interaction that follows a navigation or an API call
  • One shared WebDriverWait per page object, not per test

Summary

  • normal waits for everything, eager for the DOM, none for nothing; pair eager and none with explicit waits.
  • Set a realistic page load timeout and handle the exception by stopping the load if the page is usable.
  • readyState is not “app ready.” Wait for the element you need, or a flag the app exposes.
  • Keep implicit wait at zero so explicit waits behave predictably.

Related lessons