How to use explicit waits in Selenium

Browser automation can finish navigation before a JavaScript-driven interface exposes the next usable control. Selenium explicit waits synchronize each test action with the application state that action requires, avoiding fixed sleeps that are either too short or longer than necessary.

Python's WebDriverWait polls a condition until it returns a value that is not false or raises TimeoutException at the deadline. Locator-based expected conditions such as element_to_be_clickable() return the matching WebElement, so the next action can use the element found by the successful poll.

The local page begins with a disabled button and changes its status only after JavaScript enables the control. A second status change after the click separates button readiness from application success; keep implicit waits at their default zero unless a project requires them because Selenium warns that mixing implicit and explicit waits can produce unpredictable timeout lengths.

Steps to use explicit waits in Selenium:

  1. Create explicit-wait-demo.html with a button that becomes enabled after a short delay.
    explicit-wait-demo.html
    <!doctype html>
    <html lang="en">
    <meta charset="utf-8">
    <title>Selenium explicit wait demo</title>
    <button id="submit-order" disabled>Submit order</button>
    <p id="status">Loading order</p>
    <script>
      const button = document.querySelector("#submit-order");
      const status = document.querySelector("#status");
     
      setTimeout(() => {
        button.disabled = false;
        status.textContent = "Ready to submit";
      }, 700);
     
      button.addEventListener("click", () => {
        status.textContent = "Submitted";
      });
    </script>
    </html>
  2. Start selenium-explicit-waits-use.py with the wait imports and local page URL.
    selenium-explicit-waits-use.py
    from pathlib import Path
    import shutil
     
    from selenium import webdriver
    from selenium.webdriver.chrome.options import Options
    from selenium.webdriver.chrome.service import Service
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.support.ui import WebDriverWait
     
     
    page_url = (Path(__file__).parent / "explicit-wait-demo.html").resolve().as_uri()
  3. Extend selenium-explicit-waits-use.py after page_url with the headless Chrome session.
    options = Options()
    options.add_argument("--headless=new")
    options.add_argument("--window-size=1280,720")
     
    browser_path = shutil.which("google-chrome") or shutil.which("chromium")
    if browser_path:
        options.binary_location = browser_path
     
    driver_path = shutil.which("chromedriver")
    service = Service(driver_path) if driver_path else Service()
     
    driver = webdriver.Chrome(service=service, options=options)

    An installed ChromeDriver is used when it is on PATH. Otherwise, the empty Service() lets Selenium Manager resolve a compatible driver on supported systems.
    Related: How to configure ChromeDriver for Selenium

  4. Append the ready-state waits and button click after driver creation.
    try:
        driver.get(page_url)
        wait = WebDriverWait(driver, 5)
     
        wait.until(
            EC.text_to_be_present_in_element((By.ID, "status"), "Ready to submit"),
            "order status did not become ready",
        )
        button = wait.until(
            EC.element_to_be_clickable((By.ID, "submit-order")),
            "submit button did not become clickable",
        )
        ready_status = driver.find_element(By.ID, "status").text
        button_text = button.text
        button.click()

    Custom messages make a timeout identify the state that never appeared instead of reporting an empty wait failure.
    Related: How to troubleshoot Selenium timeout errors

  5. Complete selenium-explicit-waits-use.py with the result wait and browser cleanup.
    selenium-explicit-waits-use.py
    from pathlib import Path
    import shutil
     
    from selenium import webdriver
    from selenium.webdriver.chrome.options import Options
    from selenium.webdriver.chrome.service import Service
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.support.ui import WebDriverWait
     
     
    page_url = (Path(__file__).parent / "explicit-wait-demo.html").resolve().as_uri()
     
    options = Options()
    options.add_argument("--headless=new")
    options.add_argument("--window-size=1280,720")
     
    browser_path = shutil.which("google-chrome") or shutil.which("chromium")
    if browser_path:
        options.binary_location = browser_path
     
    driver_path = shutil.which("chromedriver")
    service = Service(driver_path) if driver_path else Service()
     
    driver = webdriver.Chrome(service=service, options=options)
    try:
        driver.get(page_url)
        wait = WebDriverWait(driver, 5)
     
        wait.until(
            EC.text_to_be_present_in_element((By.ID, "status"), "Ready to submit"),
            "order status did not become ready",
        )
        button = wait.until(
            EC.element_to_be_clickable((By.ID, "submit-order")),
            "submit button did not become clickable",
        )
        ready_status = driver.find_element(By.ID, "status").text
        button_text = button.text
        button.click()
     
        wait.until(
            EC.text_to_be_present_in_element((By.ID, "status"), "Submitted"),
            "order status did not change to Submitted",
        )
        result_status = driver.find_element(By.ID, "status").text
     
        print(f"ready: {ready_status}")
        print(f"button: {button_text}")
        print(f"result: {result_status}")
    finally:
        driver.quit()

    The final wait observes the application response rather than treating a returned click() call as proof that submission finished.

  6. Run selenium-explicit-waits-use.py to confirm both the ready state and submitted result.
    $ python3 selenium-explicit-waits-use.py
    ready: Ready to submit
    button: Submit order
    result: Submitted