stagehand

The SDK For Browser Agents

23,925 stars TypeScript Markdown Skills API Spec #agents#ai#llms#playwright
AI Prompts & Specs

Repository: browserbase/stagehand


Stars: 22115

.cursorrules

Stagehand Project

This is a project that uses Stagehand V3, a browser automation framework with AI-powered act, extract, observe, and agent methods.

The main class can be imported as Stagehand from @browserbasehq/stagehand.

Key Classes:

- Stagehand: Main orchestrator class providing act, extract, observe, and agent methods
- context: A V3Context object that manages browser contexts and pages
- page: Individual page objects accessed via stagehand.context.pages()[i] or created with stagehand.context.newPage()

Initialize

typescript
import { Stagehand } from "@browserbasehq/stagehand";

const stagehand = new Stagehand({
env: "LOCAL", // or "BROWSERBASE"
verbose: 2, // 0, 1, or 2
model: "openai/gpt-4.1-mini", // or any supported model
});

await stagehand.init();

// Access the browser context and pages
const page = stagehand.context.pages()[0];
const context = stagehand.context;

// Create new pages if needed
const page2 = await stagehand.context.newPage();

Act

Actions are called on the stagehand instance (not the page). Use atomic, specific instructions:

typescript
// Act on the current active page
await stagehand.act("click the sign in button");

// Act on a specific page (when you need to target a page that isn't currently active)
await stagehand.act("click the sign in button", { page: page2 });

Important: Act instructions should be atomic and specific:

- ✅ Good: "Click the sign in button" or "Type 'hello' into the search input"
- ❌ Bad: "Order me pizza" or "Type in the search bar and hit enter" (multi-step)

Cache the results of observe to avoid unexpected DOM changes:

typescript
const instruction = "Click the sign in button";

// Get candidate actions
const actions = await stagehand.observe(instruction);

// Execute the first action
await stagehand.act(actions[0]);

To target a specific page:

typescript
const actions = await stagehand.observe("select blue as the favorite color", {
page: page2,
});
await stagehand.act(actions[0], { page: page2 });

Extract

Extract data from pages using natural language instructions. The extract method is called on the stagehand instance.

Basic Extraction (with schema)

typescript
import { z } from "zod";

// Extract with explicit schema
const data = await stagehand.extract(
"extract all apartment listings with prices and addresses",
z.object({
listings: z.array(
z.object({
price: z.string(),
address: z.string(),
}),
),
}),
);

console.log(data.listings);

Simple Extraction (without schema)

typescript
// Extract returns a default object with 'extraction' field
const result = await stagehand.extract("extract the sign in button text");

console.log(result);
// Output: { extraction: "Sign in" }

// Or destructure directly
const { extraction } = await stagehand.extract(
"extract the sign in button text",
);
console.log(extraction); // "Sign in"

Targeted Extraction

Extract data from a specific element using a selector:

typescript
const reason = await stagehand.extract(
"extract the reason why script injection fails",
z.string(),
{ selector: "/html/body/div[2]/div[3]/iframe/html/body/p[2]" },
);

URL Extraction

When extracting links or URLs, use z.string().url():

typescript
const { links } = await stagehand.extract(
"extract all navigation links",
z.object({
links: z.array(z.string().url()),
}),
);

Extracting from a Specific Page

typescript
// Extract from a specific page (when you need to target a page that isn't currently active)
const data = await stagehand.extract(
"extract the placeholder text on the name field",
{ page: page2 },
);

Observe

Plan actions before executing them. Returns an array of candidate actions:

typescript
// Get candidate actions on the current active page
const [action] = await stagehand.observe("Click the sign in button");

// Execute the action
await stagehand.act(action);

Observing on a specific page:

typescript
// Target a specific page (when you need to target a page that isn't currently active)
const actions = await stagehand.observe("find the next page button", {
page: page2,
});
await stagehand.act(actions[0], { page: page2 });

Agent

Use the agent method to autonomously execute complex, multi-step tasks.

Basic Agent Usage

typescript
const page = stagehand.context.pages()[0];
await page.goto("https://www.google.com");

const agent = stagehand.agent({
model: "google/gemini-2.0-flash",
executionModel: "google/gemini-2.0-flash",
});

const result = await agent.execute({
instruction: "Search for the stock price of NVDA",
maxSteps: 20,
});

console.log(result.message);

Computer Use Agent (CUA)

For more advanced scenarios using computer-use models:

typescript
const agent = stagehand.agent({
mode: "cua", // Enable Computer Use Agent mode
model: "anthropic/claude-sonnet-4-20250514",
// or "google/gemini-2.5-computer-use-preview-10-2025"
systemPrompt: You are a helpful assistant that can use a web browser.
Do not ask follow up questions, the user will trust your judgement.
,
});

await agent.execute({
instruction: "Apply for a library card at the San Francisco Public Library",
maxSteps: 30,
});

Agent with Custom Model Configuration

typescript
const agent = stagehand.agent({
model: {
modelName: "google/gemini-2.5-computer-use-preview-10-2025",
apiKey: process.env.GEMINI_API_KEY,
},
systemPrompt: You are a helpful assistant.,
});

Agent with Integrations (MCP/External Tools)

typescript
const agent = stagehand.agent({
integrations: [https://mcp.exa.ai/mcp?exaApiKey=${process.env.EXA_API_KEY}],
systemPrompt: You have access to the Exa search tool.,
});

Advanced Features

DeepLocator (XPath Targeting)

Target specific elements across shadow DOM and iframes:

typescript
await page
.deepLocator("/html/body/div[2]/div[3]/iframe/html/body/p")
.highlight({
durationMs: 5000,
contentColor: { r: 255, g: 0, b: 0 },
});

Multi-Page Workflows

typescript
const page1 = stagehand.context.pages()[0];
await page1.goto("https://example.com");

const page2 = await stagehand.context.newPage();
await page2.goto("https://example2.com");

// Act/extract/observe operate on the current active page by default
// Pass { page } option to target a specific page
await stagehand.act("click button", { page: page1 });
await stagehand.extract("get title", { page: page2 });


README.md

<div id="toc" align="center" style="margin-bottom: 0;">
<ul style="list-style: none; margin: 0; padding: 0;">
<a href="https://stagehand.dev">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="media/dark_logo.png" />
<img alt="Stagehand" src="media/light_logo.png" width="200" style="margin-right: 30px;" />
</picture>
</a>
</ul>
</div>
<p align="center">
<strong>The AI Browser Automation Framework</strong><br>
<a href="https://docs.stagehand.dev">Read the Docs</a>
</p>

<p align="center">
<a href="https://github.com/browserbase/stagehand/tree/main?tab=MIT-1-ov-file#MIT-1-ov-file">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="media/dark_license.svg" />
<img alt="MIT License" src="media/light_license.svg" />
</picture>
</a>
<a href="https://stagehand.dev/discord">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="media/dark_discord.svg" />
<img alt="Discord Community" src="media/light_discord.svg" />
</picture>
</a>
</p>

<p align="center">
<a href="https://trendshift.io/repositories/12122" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12122" alt="browserbase%2Fstagehand | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
</p>

<p align="center">
<a href="https://deepwiki.com/browserbase/stagehand">
<img alt="Ask DeepWiki" src="https://deepwiki.com/badge.svg" />
</a>
</p>

<p align="center">
If you're looking for the Python implementation, you can find it
<a href="https://github.com/browserbase/stagehand-python"> here</a>
</p>

<div align="center" style="display: flex; align-items: center; justify-content: center; gap: 4px; margin-bottom: 0;">
<b>Vibe code</b>
<span style="font-size: 1.05em;"> Stagehand with </span>
<a href="https://director.ai" style="display: flex; align-items: center;">
<span>Director</span>
</a>
<span> </span>
<picture>
<img alt="Director" src="media/director_icon.svg" width="25" />
</picture>
</div>

What is Stagehand?

Stagehand is a browser automation framework used to control web browsers with natural language and code. By combining the power of AI with the precision of code, Stagehand makes web automation flexible, maintainable, and actually reliable.

Why Stagehand?

Most existing browser automation tools either require you to write low-level code in a framework like Selenium, Playwright, or Puppeteer, or use high-level agents that can be unpredictable in production. By letting developers choose what to write in code vs. natural language (and bridging the gap between the two) Stagehand is the natural choice for browser automations in production.

1. Choose when to write code vs. natural language: use AI when you want to navigate unfamiliar pages, and use code when you know exactly what you want to do.

2. Go from AI-driven to repeatable workflows: Stagehand lets you preview AI actions before running them, and also helps you easily cache repeatable actions to save time and tokens.

3. Write once, run forever: Stagehand's auto-caching combined with self-healing remembers previous actions, runs without LLM inference, and knows when to involve AI whenever the website changes and your automation breaks.

Getting Started

Start with Stagehand with one line of code, or check out our Quickstart Guide for more information:

bash
npx create-browser-app

Example

Here's how to build a sample browser automation with Stagehand:

typescript
// Stagehand's CDP engine provides an optimized, low level interface to the browser built for automation
const page = stagehand.context.pages()[0];
await page.goto("https://github.com/browserbase");

// Use act() to execute individual actions
await stagehand.act("click on the stagehand repo");

// Use agent() for multi-step tasks
const agent = stagehand.agent();
await agent.execute("Get to the latest PR");

// Use extract() to get structured data from the page
const { author, title } = await stagehand.extract(
"extract the author and title of the PR",
z.object({
author: z.string().describe("The username of the PR author"),
title: z.string().describe("The title of the PR"),
}),
);

Documentation

Visit docs.stagehand.dev to view the full documentation.


Build and Run from Source

bash
git clone https://github.com/browserbase/stagehand.git
cd stagehand
pnpm install
pnpm run build
pnpm run example # run the blank script at ./examples/example.ts

Stagehand is best when you have an API key for an LLM provider and Browserbase credentials. To add these to your project, run:

bash
cp .env.example .env
nano .env # Edit the .env file to add API keys

Installing from a branch

You can install and build Stagehand directly from a github branch using gitpkg

In your project's package.json set:

json
"@browserbasehq/stagehand": "https://gitpkg.now.sh/browserbase/stagehand/packages/core?<branchName>",


Contributing

NOTE

We highly value contributions to Stagehand! For questions or support, please join our Discord community.

At a high level, we're focused on improving reliability, extensibility, speed, and cost in that order of priority. If you're interested in contributing, bug fixes and small improvements are the best way to get started. For more involved features, we strongly recommend reaching out to Miguel Gonzalez or Paul Klein in our Discord community before starting to ensure that your contribution aligns with our goals.

<!-- For more information, please see our Contributing Guide. -->

Acknowledgements

We'd like to thank the following people for their major contributions to Stagehand:
- Paul Klein
- Sean McGuire
- Miguel Gonzalez
- Sameel Arif
- Thomas Katwan
- Filip Michalsky
- Anirudh Kamath
- Jeremy Press
- Navid Pour

License

Licensed under the MIT License.

Copyright 2025 Browserbase, Inc.