VLearnVibium

Vibium Waiting Strategies (Best Practices)

Master Vibium waiting strategies in Python — rely on auto-waiting find(), wait for elements, network responses, and page state instead of brittle sleep() calls.

By Pramod Dutta··5 min read·Verified with Vibium 26.2
▶ Animated overview · made with Remotion

The best waiting strategy in Vibium is usually no explicit wait at all: find() and every action auto-wait until the element exists and is actionable, so asserting on the element you need is the wait. Vibium runs actionability checks server-side in its Go engine — visible, stable, receives events, enabled — and polls them up to a 30-second default timeout before clicking, typing, or reading. That removes the brittle time.sleep() calls that cause flaky tests in older tools. When the signal is not a single element, Vibium gives you targeted explicit waits: wait_until() for an element to reach a state, wait_for_response() for a network call, and wait_for_function() for arbitrary page state. Each waits for a real condition and proceeds the instant it is met, so fast machines run fast and slow CI runners still pass. The rule of thumb: lean on auto-wait, reach for an explicit wait only when no element captures the condition, and never guess a duration with a fixed sleep.

How does Vibium's auto-waiting work?

Vibium auto-waits before every action. When you call find() and then click(), the Go engine polls until the element is visible, stable (its bounding box unchanged over 50ms), able to receive pointer events, and enabled — only then does it act, up to a 30-second timeout. You write no retry logic.

from vibium import browser_sync as browser
 
vibe = browser.launch()
vibe.go("https://example.com/app")
 
# No sleep needed — click() waits until the button is actionable.
vibe.find(role="button", text="Load data").click()
 
# find() waits until the first result row exists and is actionable.
print(vibe.find(".result-row").text())
 
vibe.quit()

Because this logic lives in the engine rather than the client, every Vibium language gets identical timing behavior. For the full set of checks, see how actionability works.

How do I wait for an element to appear or disappear?

When you need a specific element state — a spinner detaching, a banner becoming visible — use wait_until() on the element. It polls until the element reaches visible, hidden, attached, or detached.

from vibium import browser_sync as browser
 
vibe = browser.launch()
vibe.go("https://example.com/app")
 
vibe.find(role="button", text="Save").click()
 
# Wait for the loading spinner to be removed before reading the result.
vibe.find(".spinner").wait_until("detached")
print(vibe.find(".save-confirmation").text())
 
vibe.quit()

Waiting for a spinner to detach is a precise, readable way to know an in-flight action has settled — far better than guessing how long it takes. This is the right tool when the condition maps to one element's lifecycle.

How do I wait for a network response?

Use wait_for_response(pattern) when you must confirm the API call itself finished — for example before asserting on the exact data it returned. It blocks until a response whose URL matches the pattern completes.

from vibium import browser_sync as browser
 
vibe = browser.launch()
vibe.go("https://example.com/dashboard")
 
vibe.find(role="button", text="Load data").click()
 
# Block until the matching API response completes, then inspect it.
response = vibe.wait_for_response("**/api/data**")
print("status:", response.status())
print("payload:", response.json())
 
vibe.quit()

This waits on the network layer through WebDriver BiDi, giving you the response object with status(), headers(), and json(). It is ideal for confirming an endpoint returned 200 with the right body before you check how the UI renders it. See how to wait for AJAX content for the full pattern.

How do I wait for arbitrary page state?

When the signal is not a single element or a network call — a counter reaching a threshold, a class toggling — use wait_for_function() to poll a JavaScript condition until it returns truthy.

from vibium import browser_sync as browser
 
vibe = browser.launch()
vibe.go("https://example.com/app")
 
vibe.find(role="button", text="Refresh").click()
 
# Wait until at least 10 items have rendered.
vibe.wait_for_function("document.querySelectorAll('.item').length >= 10")
 
# Or wait until the spinner is gone from the DOM.
vibe.wait_for_function("!document.querySelector('.spinner')")
 
vibe.quit()

wait_for_function() re-evaluates the expression in the page until it is true or the timeout hits, which makes it the right tool for conditions that no single locator captures. Use it sparingly — an element find() or wait_until() is clearer when one applies — but it is invaluable for arbitrary state checks.

How do I tune the timeout?

The default actionability timeout is 30 seconds, which suits most pages, but you can shorten it for a known-fast element or lengthen it for a slow one by passing a timeout to the action. A tighter timeout makes a genuinely-broken page fail fast instead of hanging.

# Fail fast if this button is not clickable within 5 seconds.
vibe.find(role="button", text="Submit").click(timeout=5000)

Tuning per-action keeps the global default sensible while letting individual steps express their real expectations — fast paths fail quickly, known-slow paths get the headroom they need.

Why should I never use time.sleep()?

A fixed time.sleep() is the number-one cause of flaky browser tests: pick a value too short and the script reads before content lands; too long and every test pays the cost on every run, including in CI. Vibium's auto-waiting find(), plus wait_until(), wait_for_response(), and wait_for_function(), all wait for the actual condition and proceed the instant it is met — so fast machines run fast and slow runners still pass. Reserve a real sleep for the rare case where nothing observable changes, and reach for a condition-based wait everywhere else. This single discipline is what keeps a structured suite stable across local runs and CI pipelines alike.

Which waiting strategy should I use?

A quick decision guide:

  • Acting on or reading an element → just find(); auto-wait handles it.
  • An element changing state (spinner gone, modal shown) → wait_until("detached" / "visible" / ...).
  • A network call must finishwait_for_response(pattern).
  • Arbitrary page state (counts, flags) → wait_for_function(js).
  • A guessed duration → never; use one of the above.

Default to auto-wait, escalate to the most specific explicit wait the condition demands, and you get tests that are both fast and reliable.

Next steps

Frequently asked questions

How does waiting work in Vibium?

Vibium auto-waits by default. Every find() and action like click() polls until the element exists and is actionable, up to a 30-second timeout. You rarely write explicit waits because asserting on the element you need is itself the wait you would otherwise add by hand.

What is actionability in Vibium?

Actionability is the set of checks Vibium runs before an action: the element must be visible, stable, able to receive events, and enabled. The Go engine polls these conditions and only acts once they pass, which is why clicks never fire on hidden or moving elements.

Why should I avoid time.sleep() in Vibium?

Fixed sleeps either waste time or race the page: too short and the script reads before content arrives, too long and every run pays the cost. Vibium's auto-wait and explicit waits poll for the real condition, so they proceed the instant it is met on fast and slow machines alike.

Vibium is created by Jason Huggins. This is an independent tutorial — see the official Vibium site and GitHub repo for canonical docs.

Related guides