How Vibium's Actionability (Auto-Wait) Works
Vibium's actionability auto-wait runs checks (visible, stable, receives events, enabled, editable) server-side in Go before each action — skip sleeps.
Vibium's actionability is its auto-wait system: before performing any action, the Go binary polls until the target element satisfies a set of conditions — visible, stable, receives events, enabled, and (for fills) editable — up to a default 30-second timeout. Borrowed from Playwright and implemented server-side in Go, these checks mean you almost never write sleep() or retry loops. When you call el.click(), Vibium waits for the button to actually be clickable: not hidden, not still animating, not covered by a loading spinner, and not disabled. If the conditions are met, it acts; if the timeout passes first, it raises a clear timeout error instead of clicking the wrong thing. Because the logic lives in the single Go binary, every client — Python, JavaScript, and the MCP server — gets identical timing behavior.
What are the actionability checks?
Vibium verifies up to five conditions, and different actions require different subsets. Each check exists to prevent a specific class of flaky failure.
| Check | What it means | Why it matters |
|---|---|---|
| Visible | Non-zero size, not display:none or visibility:hidden | You can't interact with invisible things |
| Stable | Bounding box unchanged over 50ms | Clicking a moving target misses |
| Receives events | The element (or a child) is what's at its center point | Clicks would hit a covering overlay |
| Enabled | Not disabled, aria-disabled, or in a disabled <fieldset> | Disabled controls ignore input |
| Editable | Accepts text and isn't read-only (fills only) | Only relevant for typing into fields |
Which actions run which checks?
Each action runs only the checks that make sense for it, so Vibium doesn't over-wait. A click needs the element to receive pointer events; a fill (which sets the value programmatically) does not.
| Action | Checks applied |
|---|---|
| Click, double-click, tap, check, type | Visible + Stable + Receives events + Enabled |
| Hover, drag | Visible + Stable + Receives events |
| Fill | Visible + Enabled + Editable |
| Select option | Visible + Enabled |
| Scroll into view | Stable |
Typing actions click to focus first, so they use the click set. fill skips the "receives events" check because it sets the value via JavaScript rather than simulating keystrokes — but it does require the field to be editable.
How does the polling loop work?
Vibium runs the actionability checks in a loop until they pass or the timeout is reached, sleeping briefly between attempts. The element-side checks run in a single BiDi round-trip per attempt for speed.
deadline = now + timeout (default 30s)
loop:
run actionability script
if failed or not found:
if past deadline: raise TimeoutError
sleep 100ms; continue
if stability is required:
sleep 50ms
run script again
if bounding boxes differ:
if past deadline: raise TimeoutError
sleep 100ms; continue
return element (ready to act)
Stability is the one check that runs partly on the Go side, because it needs a time delay: Vibium measures the bounding box, waits 50ms, measures again, and only proceeds if the box hasn't moved. Everything else runs inside the page in one shot.
What does this look like in code?
Because waiting is automatic, your script reads like a description of intent, with no timing scaffolding. Here is a login flow using the verified Python sync API:
from vibium import browser_sync as browser
vibe = browser.launch()
vibe.go("https://app.example.com/login")
# Each call auto-waits for actionability — no sleeps needed
vibe.find("#username").type("alice")
vibe.find("#password").type("s3cret")
vibe.find(role="button", text="Sign in").click()
print(vibe.find(".welcome").text())
vibe.quit()Notice there is no wait_for before the click. If the "Sign in" button is briefly disabled while the form validates, Vibium simply keeps polling until it becomes enabled, then clicks.
How do I scroll, and what about off-screen elements?
You rarely need to scroll manually — Vibium automatically scrolls an element into view before running its checks. The actionability script calls Chrome's scrollIntoViewIfNeeded (falling back to scrollIntoView) so an off-screen button gets a fair chance to become visible before the visibility and hit-testing checks run. That means a click() on an element below the fold just works.
Can I change or shorten the timeout?
Yes. The default is 30 seconds, but you can override it per action when you know an element should appear fast and want to fail quickly. In JavaScript:
await page.find('#submit').click({ timeout: 5000 }) // wait up to 5sA shorter timeout is useful in tests where a missing element should be reported as a failure rather than hanging for the full 30 seconds.
Why server-side actionability matters
Putting actionability in the binary is what lets the clients stay trivial. The waiting logic is written once in Go, runs over a fast local connection to the browser, and gives Python, JavaScript, and AI agents the same behavior. That is also why the accessibility-tree finders and semantic selectors feel so robust — they ride on the same actionability engine. You describe the element and the action; Vibium handles the timing.
Next steps
Frequently asked questions
What is actionability in Vibium?
Actionability is Vibium's auto-wait system: a set of conditions an element must satisfy before an action runs. Vibium polls until the element is visible, stable, receiving events, enabled, and (for fills) editable, up to a 30-second default timeout, so your scripts don't need manual sleeps or retries.
What checks does Vibium run before clicking?
Before a click, Vibium verifies four conditions: the element is visible (non-zero size, not hidden by CSS), stable (its bounding box hasn't moved over 50ms), receives events (not covered by an overlay), and enabled (not disabled or aria-disabled). All must pass within the timeout.
How do I change Vibium's auto-wait timeout?
The default actionability timeout is 30 seconds. You can override it per action by passing a timeout. For example, in JavaScript page.find('#submit').click({ timeout: 5000 }) waits up to 5 seconds. If the element never becomes actionable, Vibium raises a timeout error.
Vibium is created by Jason Huggins. This is an independent tutorial — see the official Vibium site and GitHub repo for canonical docs.
Related guides
WebDriver BiDi vs CDP: What's the Difference?
WebDriver BiDi vs CDP: both are bidirectional protocols, but BiDi is a cross-browser W3C standard while CDP is Chrome-specific and vendor-controlled.
4 min read→Concepts & InternalsEvent-Driven Browser Automation with Vibium
Event-driven browser automation with Vibium uses WebDriver BiDi's pushed events for auto-waiting, console capture, and reliable scripts without sleeps.
4 min read→Concepts & InternalsSelf-Healing Locators in Vibium, Explained
Vibium has no bolt-on self-healing locator engine — it earns resilience via semantic selectors, accessibility-tree finding, pickBest, and auto-wait.
4 min read→Concepts & InternalsSync vs Async in Vibium: How It Works Under the Hood
Sync vs async in Vibium under the hood: both clients call the same Go binary over a local WebSocket, so blocking and awaiting share one automation engine.
4 min read→