Explicit wait in Selenium pauses a test until one specific condition becomes true for one specific element, instead of waiting on a fixed timer.
Quick answer: Explicit wait = wait for a specific condition on a specific element, usually written as WebDriverWait + ExpectedConditions. It differs from implicit wait, which applies one blanket timeout to every element lookup in the session.
Most guides to explicit wait give you one WebDriverWait snippet and call it done. The snippet is the easy part. What actually decides whether your suite is stable is which condition you wait for, how explicit wait interacts with the implicit wait you may have set elsewhere, and whether your code still uses the pre-Selenium 4 syntax that quietly went stale a few years ago.
If a test clicks a button a fraction of a second before it is interactive and throws ElementNotInteractableException, you have met the problem explicit wait solves. It pauses your script until a specific condition is true, so you act on the page when it is ready rather than when the clock says so.
Here is what we will cover:
Explicit wait pauses test execution until a specific condition is met or a timeout is reached, then continues as soon as the condition is true. Unlike an implicit wait, which applies one blanket timeout to every element lookup, an explicit wait targets a single condition on a single element, such as becoming visible, clickable, or present in the DOM.
In practice, you create an explicit wait with the WebDriverWait class and hand it an expected condition. So when people say "explicit wait," they almost always mean a WebDriverWait paired with an ExpectedConditions check. That is the tool you will reach for most often in real test code.
Selenium gives you three wait types, and the most common cause of flaky timing is treating them as interchangeable. Here is how they actually differ.
| Wait type | Scope | What it does | Reach for it when |
|---|---|---|---|
| Implicit wait | Global, whole session | Sets a default poll time the driver applies to every findElement call before throwing NoSuchElementException | You want a simple baseline and nothing more. Set it once, then leave it alone. |
| Explicit wait | One element or condition | Waits for a specific ExpectedCondition (visible, clickable, text present) up to a timeout | You are waiting on a known condition for a known element. This is the default tool for most waits. |
| Fluent wait | One element or condition | An explicit wait with manual control over polling frequency and ignored exceptions | The defaults are not enough. You need a custom poll interval or to ignore a specific exception while polling. |
The short version: implicit wait handles general delays, explicit wait handles precise conditions, and fluent wait is explicit wait with the dials exposed. We go deep on the third one in the guide to Fluent Wait in Selenium.
Before you add any wait strategy, it helps to know what Selenium does on its own: nothing. Out of the box, Selenium checks for an element exactly once. If it is not there yet, Selenium throws NoSuchElementException immediately instead of giving the page a chance to catch up.
You can see this baseline directly by setting the implicit wait to zero. It is also the right way to confirm a test is not quietly inheriting a leftover implicit wait from earlier setup code.
import java.time.Duration;
driver.manage().timeouts().implicitlyWait(Duration.ZERO);
driver.implicitly_wait(0)
This is why explicit wait exists in the first place. Without it, and without an implicit wait set somewhere, every element lookup on a dynamic page is a race between your script and the browser, and the browser is not guaranteed to win.
This is the current Selenium 4 setup. Note the Duration, which matters and is covered below.
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement loginBtn = wait.until(
ExpectedConditions.elementToBeClickable(By.id("login"))
);
loginBtn.click();
The wait checks for the login button to become clickable, returns it the moment it is ready, and throws a TimeoutException if 10 seconds pass without it. You act on the element only when Selenium confirms the condition, not on a fixed guess.
The Python binding uses the same WebDriverWait class with the expected_conditions module, conventionally imported as EC.
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
wait = WebDriverWait(driver, timeout=10)
login_btn = wait.until(EC.element_to_be_clickable((By.ID, "login")))
login_btn.click()
Same behavior as the Java version. The locator is passed as a tuple, (By.ID, "login"), which is the Python convention that trips up people arriving from Java.
Explicit wait is only as good as the condition you give it, and most flaky waits come from picking the wrong one. These three are easy to confuse and behave differently.
Beyond those, the conditions you will reach for include textToBePresentInElementLocated, invisibilityOfElementLocated (handy for waiting out a loading spinner), alertIsPresent, frameToBeAvailableAndSwitchToIt, and titleContains. The Python module mirrors these with snake_case names such as text_to_be_present_in_element and invisibility_of_element_located.
The rule of thumb: wait for the state your next line actually needs. If you are about to click, wait for clickable, not presence.
If you learned explicit wait a few years ago, your syntax is probably deprecated. Selenium 4 replaced the old (long, TimeUnit) timeout arguments with a single Duration, across implicit waits, WebDriverWait, and FluentWait. The old forms still compile for now but show deprecation warnings, and relying on them dates your code.
import java.time.Duration;
import java.util.concurrent.TimeUnit; // only needed for the deprecated example
import org.openqa.selenium.support.ui.WebDriverWait;
// Deprecated (pre-Selenium 4)
WebDriverWait wait = new WebDriverWait(driver, 10);
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
// Selenium 4 and later
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
Import java.time.Duration and the warnings go away. The same Duration change applies to withTimeout and pollingEvery on FluentWait. Selenium 4 also moved fully to the W3C WebDriver protocol under the hood, but for waits the Duration switch is the change you will actually touch.
Every explicit wait eventually fails on some run: a slow deploy, a flaky third-party script, a locator that stopped matching. When the condition never becomes true, WebDriverWait throws TimeoutException, and your test crashes right there unless you catch it.
Catching it on purpose, rather than letting the whole run die, gives you the chance to log what happened, capture evidence, or retry before giving up.
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
try {
WebElement signupBtn = wait.until(
ExpectedConditions.elementToBeClickable(By.id("signup"))
);
signupBtn.click();
} catch (TimeoutException e) {
System.out.println("Signup button not found within timeout period");
}
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
wait = WebDriverWait(driver, timeout=10)
try:
wait.until(
EC.element_to_be_clickable((By.ID, "signup"))
).click()
except TimeoutException:
print("Signup button not found within timeout period")
Do not over-use this pattern. Catching TimeoutException to log context or retry once is reasonable; catching it just to swallow the error and continue hides a real failure instead of fixing it. If you are already building out failure evidence, our guide to taking screenshots in Selenium covers capturing the page at the exact moment a wait times out.
The syntax is rarely the problem. These are.
Most of the time, explicit wait is the right call and its defaults are fine. Reach for fluent wait only when you need something the defaults will not give you: a custom polling interval, or the ability to ignore a specific exception such as StaleElementReferenceException while the page re-renders. Since WebDriverWait is itself a subclass of FluentWait in Java, you are not switching tools so much as exposing more dials on the same one. If your dynamic pages are fighting you, the Fluent Wait guide covers the polling and exception handling in full.
Everything above is the manual approach, and for a small or highly custom suite that control is worth the code. The cost shows up at scale: you are choosing and maintaining the right condition and timeout around dynamic elements across hundreds of tests, and every one of those waits can drift or get copied slightly wrong.
Katalon Studio takes a different default. Its Smart Wait feature waits for the page to reach a static state, meaning pending AJAX calls, JavaScript, and resource loading have settled, before it performs the next action. Instead of you deciding element by element when the page is ready, "wait until the page actually stops changing" becomes the baseline for every step. It runs on Selenium 4.39 under the hood, so you keep the engine you already know, and it is on by default under Project > Settings > Execution > WebUI.
You can still be explicit when you want to. Studio's built-in keywords cover the common conditions without the boilerplate:
WebUI.waitForElementVisible(findTestObject('Page/btn_login'), 10)
WebUI.waitForElementClickable(findTestObject('Page/btn_login'), 10)
WebUI.waitForElementPresent(findTestObject('Page/result_row'), 10)
What Smart Wait removes is not waiting logic, it is the work of hand-picking and maintaining a condition and timeout for every dynamic element across a growing suite. You can still drop into explicit waits and Groovy whenever a custom condition demands it. When a wait does fail, Studio 11's Tracer records DOM snapshots, network activity, console logs, and browser state for the run, so a flaky failure leaves evidence behind instead of forcing you to guess.
If you hit an error from Katalon's own SmartWaitWebDriver rather than a plain Selenium timeout, see our guide to handling Selenium waits in Katalon for troubleshooting steps specific to that feature.
You can see it on a real page in the Smart Wait sample project, and the Smart Wait documentation covers the configuration in full.