ScreenshotNeo

BlogHow-to

How to Use Waits in Selenium with Ruby

Use Selenium Ruby explicit waits to synchronize tests with page state. Learn when to wait, how to configure retries, and how to troubleshoot timeouts.

By the ScreenshotNeo team4 October 20266 min read

Use an explicit wait when a Selenium Ruby test must pause until a particular browser condition is true. Create Selenium::WebDriver::Wait with a timeout, then pass a block to until. The wait polls the block and returns its truthy result, or raises a timeout error when the deadline passes.

wait = Selenium::WebDriver::Wait.new(timeout: 10, interval: 0.2)
wait.until { driver.find_element(id: 'submit').displayed? }
driver.find_element(id: 'submit').click

For a page that may replace the element while loading, locate it inside the block so each poll checks the current DOM. Choose a condition that matches the operation that follows: an element being present does not necessarily mean it is visible or ready for interaction.

1. Set up Selenium WebDriver for Ruby

Install the Selenium gem in your project:

gem install selenium-webdriver

Or add it to a Bundler-managed project:

# Gemfile
gem 'selenium-webdriver'
bundle install

Create a driver and navigate to a page. This runnable example uses the current Selenium Ruby API shape; a working browser and compatible driver setup are also required.

require 'selenium-webdriver'

driver = Selenium::WebDriver.for :chrome
begin
  driver.navigate.to 'https://example.com'
  wait = Selenium::WebDriver::Wait.new(timeout: 10, interval: 0.2)
  heading = wait.until do
    element = driver.find_element(css: 'h1')
    element if element.displayed?
  end
  puts heading.text
ensure
  driver.quit
end

Check the official Selenium waiting strategies guide and the version-matched Ruby Wait API reference for current details.

2. Choose the wait condition for the next action

An explicit wait is a loop around a condition you supply. It continues polling until the block returns a truthy value. Selenium’s Ruby guide demonstrates waiting for an element to be displayed before typing.

Wait for visibility before typing

search = wait.until do
  element = driver.find_element(name: 'q')
  element if element.displayed?
end
search.send_keys('selenium ruby')

Wait for a changing page value

The block can return the value needed by the next step, not just true. For example, wait for a title to include expected text:

title = wait.until do
  current = driver.title
  current if current.include?('Results')
end
puts title

Wait for an element to disappear

Return true when the element is no longer displayed. If it is removed from the DOM entirely, the default ignored missing-element exception allows the next poll to continue.

wait.until do
  !driver.find_element(css: '.loading').displayed?
end

Use the actual state your test needs. For example, if the next operation is a click, visibility may be a useful prerequisite, but the page can still change between the check and the click. Keep the action close to the wait and handle any application-specific state changes explicitly.

3. Configure timeout, polling interval, and ignored errors

The Ruby wait accepts timeout:, interval:, an optional message:, an optional message provider, and ignore: exceptions. The API reference for the Selenium gem version in your project is the authority for defaults; set values explicitly when they matter to the test.

wait = Selenium::WebDriver::Wait.new(
  timeout: 10,
  interval: 0.2,
  message: 'Submit button did not become visible',
  ignore: [Selenium::WebDriver::Error::NoSuchElementError]
)

The block below returns the element once it is displayed. It retries a missing element and a not-interactable error, as in Selenium’s documented Ruby example:

retryable_errors = [
  Selenium::WebDriver::Error::NoSuchElementError,
  Selenium::WebDriver::Error::ElementNotInteractableError
]

wait = Selenium::WebDriver::Wait.new(
  timeout: 10,
  interval: 0.2,
  ignore: retryable_errors
)

button = wait.until do
  element = driver.find_element(id: 'submit')
  element if element.displayed?
end
button.click

Only ignore errors that are expected while the page transitions. An exception outside the configured ignored list escapes immediately; broadly suppressing errors can hide a broken locator or a real test failure. A timeout and interval are examples, not universal recommendations. Pick them based on the application and test environment.

4. Explicit waits versus implicit waits

Behavior Implicit wait Explicit wait
Scope Session-wide element-location calls A particular condition in a wait block
What triggers progress An element lookup succeeds or its implicit timeout expires The block returns a truthy value
Configuration One session setting Per-wait timeout, interval, message, and ignored errors
Typical use Broad behavior for element lookups Wait for a specific page state before the next step

Selenium’s implicit wait defaults to zero. An explicit wait makes the required state visible in the test and lets you set a deadline for that condition.

Do not casually combine implicit and explicit waits. Selenium warns that mixed waits can produce unpredictable total wait times. Its guide gives an example where a 10-second implicit wait combined with a 15-second explicit wait can result in a timeout after 20 seconds. If you use explicit waits for synchronization, avoid an implicit wait configured elsewhere in your setup.

5. Common errors and fixes

Symptom Likely cause Fix
Selenium::WebDriver::Error::TimeoutError The block did not return truthy before its deadline. Check that the locator matches the intended element, the condition reflects the needed state, and the timeout fits the environment. Include a useful wait message.
The wait fails immediately with an exception The exception is not in the wait’s ignored list. By default, NoSuchElementError is ignored; other failures are not. Fix the underlying error, or add a specific transient exception to ignore: only if retrying it is appropriate.
An element is found, but typing or clicking fails Finding an element does not prove it is displayed or interactable. Wait on a condition relevant to the next action, such as displayed?, and account for page changes between waiting and acting.
Wait duration seems longer than configured An implicit wait may be active in shared setup, causing each lookup inside the explicit wait to block too. Inspect driver initialization and test helpers. Avoid mixing implicit and explicit waits.
The wait passes for a stale state The page replaced an element after it was located. Find the element inside the polling block so the condition checks the current DOM on each attempt.

6. Performance and reliability

  • Avoid fixed sleeps for changing page state. A fixed delay always waits the full duration, even if the page is ready sooner, and may still be too short on a slower run. A condition-based wait proceeds as soon as its condition succeeds.
  • Keep polling work small. Each poll executes the block and any WebDriver calls it contains. Avoid expensive or unrelated work in the condition.
  • Set bounded timeouts. A timeout gives a slow or broken page a limit and makes the failure point clear. Tune it to the test environment rather than copying an example blindly.
  • Use precise conditions. Waiting for the exact state required by the next action helps prevent both premature actions and unnecessary delay.
  • Make failures diagnosable. Supply a message and keep the condition focused so a timeout points to a specific missing state.

Explicit waits add polling and browser communication while a condition is false. There is no universal timeout or interval that fits every application, and the cited Selenium references provide API behavior rather than comparative performance benchmarks.

7. Or skip the browser setup

If your goal is to capture a page rather than interact with it in a test, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns an image or PDF. See the ScreenshotNeo API documentation for request options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
  • Cookie and consent banners are accepted like a visitor, and 60+ known consent platforms, newsletter popups, and chat widgets are removed before capture; each step can be turned off.
  • Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing. Response headers report the page verdict and whether the shot was billed.
  • An MCP server gives AI agents tools for screenshots, page information, and PDF capture.
  • The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is on every plan.

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

8. FAQ

What does wait.until return?

It returns the truthy value produced by the block, which can be an element or another result your test needs.

Why is the default ignored exception important?

A missing element is commonly transient while a page loads, so NoSuchElementError is ignored by default. Other exceptions are not automatically retried.

Should every Selenium test use an explicit wait?

Use one when the next step depends on browser state that may change asynchronously. A wait is unnecessary when the required state is already synchronous and guaranteed by the preceding operation.

No. They demonstrate configuration. Choose values for your page and execution environment, and confirm API defaults against the installed gem version.