Core concepts
Three nouns carry every WRC script: CloudBrowser, Locator, and
Wait. Once those click into place — plus a one-paragraph mental
model of the session lifecycle — the rest of the SDK is just method
discovery in the API reference.
CloudBrowseris your live handle to a remote browser session.Locatoris a declarative "which element / under which conditions"; the same object works for both waits and actions.Waitblocks until a Locator matches — but you rarely need it because every action auto-waits.- The session is billed per minute (rounded up), and credits are deducted upfront when you rent.
CloudBrowser
CloudBrowser is the gRPC-backed handle you receive from RentBrowser.
Think of it as a small, stateful client to one specific browser context:
every method on it is one round-trip to the server, and the order matters
(navigate first, click after).
browser, err := wrc.RentBrowser(ctx, cfg)
if err != nil { log.Fatal(err) }
defer browser.Close()
_, _ = browser.Navigate(ctx, "https://wrc-beta.xyz", 0)const browser = await rentBrowser(cfg);
try {
await browser.navigate("https://wrc-beta.xyz");
} finally {
await browser.stopBrowser();
}The handle is not safe to share across goroutines or async contexts — every call serialises on the underlying gRPC stream, so concurrent use just queues. If you need real parallelism, rent a second session.
Locator
A Locator answers "which element on the page do I mean?". The same
Locator object is what you pass to Click, Fill, Drag — and what you
pass to Wait. One declarative thing, two uses.
Four constructors
| Constructor | What you give it | When to reach for it |
|---|---|---|
CSS(selector) | A CSS selector string | Your default — almost every locator you ever write |
JS(expression) | A JS expression that returns an Element or a truthy value | When CSS can't describe it (text content, computed state, custom logic) |
Node(backendNodeId) | A node id returned by a previous call | Acting on something Wait / GetDOM / Evaluate already found, without re-resolving |
At(x, y) | Viewport coordinates in CSS pixels | Pixel-precise hits (canvas, captcha cells) — accepted by Click and MoveTo only |
You usually need nothing else
The thing to internalise is that CSS(...) is already complete. When
you write it, the SDK quietly attaches two sensible defaults: the element
must be visible, and its bounding rect must hold still for 500 ms before
the wait matches. That covers the 95 % case — buttons that fade in, modals
that slide, list items that lazy-load — without you thinking about it.
// 95% case — nothing extra. Auto-waits for visible + steady.
_, _ = browser.Click(ctx, wrc.CSS("button.submit"))// 95% case — nothing extra. Auto-waits for visible + steady.
await browser.click(css("button.submit"));You only reach for modifiers when those defaults need to bend:
// Element is deliberately offscreen (display:none) — opt out of visible.
_, _ = browser.Click(ctx, wrc.CSS(".lazy-mounted").Visible(false))
// You want to fire as soon as it appears — skip the 500ms steady wait.
_, _ = browser.Click(ctx, wrc.CSS(".banner").Steady(0))
// Search inside every frame instead of just the main document.
_, _ = browser.Click(ctx, wrc.CSS("button.consent").InAllFrames())// Element is deliberately offscreen (display:none) — opt out of visible.
await browser.click(css(".lazy-mounted").visible(false));
// You want to fire as soon as it appears — skip the 500ms steady wait.
await browser.click(css(".banner").steady(0));
// Search inside every frame instead of just the main document.
await browser.click(css("button.consent").inAllFrames());The full modifier set is .Visible / .Steady / .InFrame / .InAllFrames,
all chainable in any order. The Targeting elements guide goes deep on
each one.
Wait
Every element action already waits for its target — Click(ctx, CSS(...))
does not need a separate Wait call in front of it. You reach for Wait
directly only when you want to observe a state change without acting:
confirm a modal appeared, race two possible outcomes, or block on a JS
condition that is not even an element.
The Go API is variadic — conditions and options interleave freely. The TS
API splits the two: wait(condition, opts?) for one, waitAny([...], opts?)
for several.
// Either a success banner or a redirect URL — whichever comes first.
res, err := browser.Wait(ctx,
wrc.CSS(".banner-success"),
wrc.JS("location.pathname === '/welcome'"),
wrc.Timeout(5000),
)
if err != nil { log.Fatal(err) }
fmt.Println("matched condition index:", res.Index)// Either a success banner or a redirect URL — whichever comes first.
const res = await browser.waitAny(
[css(".banner-success"), js("location.pathname === '/welcome'")],
{ timeoutMs: 5000 },
);
console.log("matched condition index:", res.index);When several conditions are passed, the first one to match wins —
the others are abandoned the instant a winner is found. WaitResult.Index
(.index in TS) tells you which entry won, so you can branch on it.
Session lifecycle
RentBrowser ──▶ drive (navigate, click, …) ──▶ StopBrowser / Close
│
└── credits deducted upfront, per minute, rounded up
(refunded automatically if the rent itself fails)
Three things can end a session:
- You call
StopBrowser(orClosein Go) — the canonical, clean shutdown. Always defer this so a panic mid-script still releases the session. - The
rentDurationyou set onNewBrowserConfigexpires — the server tears the session down and any in-flight call returns an error. The full reserved window was already paid for at rent time, so stopping early does not refund the unused minutes. - The process holding the handle dies without releasing it — harmless on our side: the server reaps the session on its own after a short grace period. You still spent the upfront credits.
The cleanest pattern: defer browser.Close() (Go) or
try { … } finally { await browser.stopBrowser(); } (TS). It costs
nothing when the script runs to completion and saves a session from
hanging when something blows up.
The SDK defaults at a glance
A handful of constants govern "what does WRC do when I leave the option off?". They are exported so you can read them, and overridable per call:
| Default | Value | Where it applies |
|---|---|---|
DefaultWaitTimeoutMs | 30 000 ms (30 s) | Wait and every action's implicit wait |
DefaultVisible | true | CSS / JS element locators require the match to be visible |
DefaultSteadyMs | 500 ms | CSS / JS element locators require the bounding rect to stop moving for this long |
Translated: by default, every action waits up to 30 seconds for an
element that is on-screen and not animating. You rarely need to think
about this — the moment you do, override per locator with
.Visible(false) / .Steady(0), or per call with Timeout(...).
- The Targeting elements guide walks through every Locator constructor and modifier in depth.
- The Waiting guide covers multi-condition waits, anti-patterns and timeouts.
- The Go SDK reference and TypeScript SDK reference list every method on
CloudBrowser.