Stagehand:
Inside the AI-Driven Browser Automation Framework
Hardcoded selectors break the moment a page changes; fully autonomous agents are quick to write but hard to trust. Stagehand, the open-source browser-automation SDK from Browserbase, takes a third path - you write plain-English instructions, an AI resolves them to real elements at runtime, and deterministic code does the clicking. Explore how its four verbs work, why it caches the AI's answers, and what it gave up to leave Playwright behind.
Your scraper ran flawlessly for eight months. Then, one Tuesday night, a frontend
developer at the site you automate renamed a CSS class - .btn--buy became
.purchase-cta - and your pipeline died without a sound. Nothing crashed
loudly. The script looked for a button that no longer existed, timed out, retried,
timed out again. You found out Thursday, from a stakeholder asking where the data went.
This is the standing bargain of traditional browser automation. Tools like Playwright, Selenium, and Puppeteer are exact: you tell them precisely which element to click, and they click it fast, deterministically, for free. But that exactness is also the failure mode. A hardcoded selector is a bet that the page will never change, and the page always changes. Every automation you write is quietly accruing a maintenance debt that comes due at the least convenient time.
At the other extreme sits the fully autonomous AI agent. You hand it a goal - "buy the headphones" - and it looks at the page, reasons about it, and figures out the clicks on its own. Redesigns don't faze it. But now you have a different problem: the agent might take a different path every run, it's hard to debug when it wanders, and every step burns model tokens. You've traded brittle for black box.
Stagehand is an open-source
framework from Browserbase (the
company that sells headless browser infrastructure) built on a wager: the right unit of
automation is neither the selector nor the goal, but the instruction. MIT-licensed,
with over 23,000 GitHub stars, it bills itself as "the SDK for browser agents." You write
act("click the buy button"), and an AI resolves that sentence at runtime to
whatever the buy button happens to be today - then ordinary, deterministic browser code
performs the click.
Developers use Stagehand to reliably automate the web.
Stagehand documentationTo feel the difference, try breaking something. The figure below shows the same tiny storefront automated two ways: a hardcoded selector on the left, a Stagehand-style instruction on the right. Run both, then ship a redesign and run them again - watch which one survives.
Four Verbs for a Browser
A stagehand, in the theater, is the person in the wings who moves the props so the show can go on. The framework earns its name with a deliberately small API: four verbs that cover everything you'd ask of a browser.
The first three are the precision tools. act() executes exactly one step;
the docs are emphatic about keeping instructions atomic - "click the login button", not
"log in and go to settings". extract() turns scraping into a typed function
call: you hand it a Zod schema and get back an object that actually conforms to it (see
the box below if Zod is new to you). observe() is reconnaissance - it tells
you what's actionable on the page before anything irreversible happens.
Zod is a TypeScript library for describing the shape of data. You spell out each field and its type once; Stagehand shows that description to the model as the thing to fill in, then checks the model's answer against it before handing the object back. A page that returns the wrong shape raises an error instead of a silently-wrong result.
The fourth verb changes who's driving. agent() takes a goal and loops -
observing, deciding, acting - until the goal is met. You can back it with a regular LLM
or a dedicated computer-use model:
One more detail worth knowing early: instructions are sent to a model provider, so
Stagehand gives you placeholder variables for anything sensitive. You write
act("type %password% into the password field", { variables: { password: ... } })
and the secret is substituted locally - the LLM only ever sees the placeholder.
The tension between the two modes is the interesting part. The precision verbs are predictable and debuggable but need you to write the route; the agent finds the route but is only as predictable as its model. Stagehand's own docs describe how most teams square it: use the agent for exploration, and pin the critical paths down with individual primitives. The figure below puts all four verbs on the same page - literally. Switch tabs and watch what each one does to the same little store.
From Instruction to Click
So what actually happens inside act("click the buy button")? It can feel
like magic, but each stage turns out to be a piece of understandable
engineering - a short pipeline you can follow from end to end.
First, Stagehand needs to show the model the page, and it does not send a screenshot
or a raw HTML dump by default. It reads the page's accessibility tree - the
semantic outline the browser already maintains for screen readers, where a
<div class="x7-btn"> becomes simply button, "Buy now".
(You can see this directly: calling extract() with no arguments returns
the accessibility tree itself.) It's a beautifully economical choice - the tree is
orders of magnitude smaller than the DOM, and it describes the page the way a person
would: by role and label, not by markup.
The model receives your instruction plus that tree and picks a target and a method -
click, fill, type, press, scroll, or select. Crucially, the decision materializes as a
concrete, boring artifact: the ActResult that act() returns
records the XPath selectors that were used, alongside success status and a description
of what happened. The AI's judgment gets compiled down to something you can log,
inspect, and - as the next section shows - cache. Then deterministic code dispatches
the actual event, traversing iframes and shadow DOM on its own when the target lives
inside one.
This is also why instruction phrasing matters more than it might seem. The docs push
you toward specificity - "the red Delete button next to user John Smith" beats "click
the button" - because the model is choosing among candidates, and ambiguity in, wrong
element out. For anything high-stakes, there's a preview idiom: observe()
returns the resolved action without executing it, and you pass it to
act() only if you like what you see.
Step through the pipeline yourself below. Each press of the button advances the instruction one stage further toward a real click.
act() call. Watch the payload panel
below the pipeline: the fuzzy natural-language instruction becomes a filtered
accessibility tree, then a model decision, then a concrete XPath - and only then
does anything touch the browser.
Write Once, Run Forever
There's an obvious objection to everything above: an LLM call per action is slow and costs money. If your automation runs every fifteen minutes, you do not want to pay a model to re-discover the same buy button 96 times a day.
Stagehand's answer is aggressive caching, and it's where the framework's "write once,
run forever" pitch comes from. The first time an instruction resolves, the result -
that concrete XPath from the previous section - is stored. Every subsequent run replays
the stored action directly, skipping inference entirely. Locally, you opt in by pointing
the constructor at a cache directory; on Browserbase's cloud, a server-side cache is on
by default, and every result tells you whether it was a "HIT" or a
"MISS":
The clever part is what happens when the cache goes stale. That redesign from the opening scene lands, the stored XPath stops matching, and a classic script would be dead. Stagehand instead detects that the page has changed, re-runs inference against the new page, and repairs its own cache - the behavior Browserbase calls self-healing. The redesign that used to be a Thursday-morning incident becomes one cache miss and one extra model call.
Run the simulation below a few times and watch the economics play out. The first run is slow and costs tokens. The next runs are fast and free. Then ship a redesign and see the system take the hit once - and only once.
Graduating from Playwright
Stagehand didn't start as its own automation engine. When Browserbase open-sourced it
in 2024, it was a layer on top of Playwright - which was exactly why it caught on. You
could drop act() and extract() into an existing Playwright
script in minutes, keeping everything else you'd built. By version 2 the package was
seeing over 500,000 weekly downloads.
But scale surfaced a mismatch in DNA. Playwright is a testing tool. Before it clicks anything, it runs actionability checks - waiting for the element to be, in Playwright's terms, "visible, stable, enabled, and receiving events." For a CI suite, that caution is the whole point. For a high-throughput automation clicking a button it resolved and cached weeks ago, it's latency tax on every single action. The Browserbase team also kept hitting lower-level friction: Playwright doesn't expose stable frame identities, which made it awkward to route Chrome DevTools Protocol commands at specific iframes; its CDP sessions are scoped to pages and contexts rather than frames; and users reported memory creep in long-lived sessions.
So in October 2025, Stagehand v3 removed the middleman. The engine was rewritten to speak Chrome DevTools Protocol directly - the same raw websocket protocol the browser's own DevTools use - streaming accessibility trees, DOM snapshots, and network events straight from the browser with no translation layer in between. On Browserbase's benchmark of deeply nested iframes and shadow DOMs, v3 measured 44.11% faster on average than the Playwright-based architecture. And in a nice twist, shedding the dependency made Stagehand more compatible, not less: the v3 API is driver-agnostic, working alongside Playwright and Puppeteer rather than living inside one of them, with SDKs beyond TypeScript and a REST API.
The figure below shows the architectural difference as message traffic. Both lanes perform the same repeated action; count the hops.
The Honest Trade-offs
None of this is free, and Stagehand is refreshingly legible about where the costs moved. Every cache miss is a metered LLM call, so a fleet of automations against frequently-changing pages runs up a real inference bill. The project doesn't recommend local models, so in practice you're wiring in a cloud model API. And the engine is Chromium-only - Chrome, Edge, Arc, Brave - so cross-browser testing stays with the traditional tools.
The sharpest comparison is with Browser Use, the Python framework that owns the other end of the spectrum from figure 1. Browser Use hands the whole task to an agent that works from screenshots - the AI sees what a person sees and decides each next move. It has the larger community (roughly 80,000 GitHub stars to Stagehand's 23,000) and it supports local inference through Ollama, which makes it effectively free to run. The price is variance: an autonomous agent may take a different route each run, and multi-step autonomy burns more tokens per task than atomic, cacheable operations.
Stagehand
- TypeScript-first; SDKs in more languages since v3
- Step-by-step control, cacheable and replayable
- Accessibility-tree context; direct CDP engine
- Strong on nested iframes and shadow DOM
- Cloud LLM APIs; local models not recommended
Browser Use
- Python-first, fits data-science stacks
- Fully autonomous, screenshot-based agent
- Playwright underneath
- Local inference via Ollama - zero API cost
- More tokens per task, more run-to-run variance
Scrapfly's independent review lands on a clean split: reach for Browser Use when you want autonomy, Python, or free local inference; reach for Stagehand when you need predictable, debuggable, production-grade runs - especially anywhere iframes and shadow DOM lurk. That matches Stagehand's own advice: "agent for exploration, individual primitives for critical paths."
Which brings us back to that Tuesday night. With a hardcoded selector, the renamed button was a silent outage and a Thursday post-mortem. With an instruction, it's a cache miss: one extra model call, a healed cache, a log line you read later with your coffee. The stagehand moves the props between scenes, and the show goes on.