Documentation Index
Fetch the complete documentation index at: https://mintlify.com/browserbase/stagehand/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Stagehand can work with multiple browser tabs (pages) simultaneously. This guide covers patterns for multi-tab workflows.
Creating New Pages
Basic Page Creation
const stagehand = new Stagehand({ env: "LOCAL" });
await stagehand.init();
// Get the first page (created automatically)
const page1 = stagehand.context.pages()[0];
await page1.goto("https://example.com");
// Create a second page
const page2 = await stagehand.context.newPage();
await page2.goto("https://another-site.com");
// Create a third page
const page3 = await stagehand.context.newPage();
await page3.goto("https://third-site.com");
List All Pages
const allPages = stagehand.context.pages();
console.log(`Total pages: ${allPages.length}`);
for (const page of allPages) {
console.log(`Page URL: ${page.url()}`);
}
Operating on Specific Pages
Specify Page in Operations
const page1 = stagehand.context.pages()[0];
const page2 = await stagehand.context.newPage();
await page1.goto("https://store.com");
await page2.goto("https://comparison-site.com");
// Act on page1
await stagehand.act("search for 'laptop'", { page: page1 });
// Act on page2
await stagehand.act("search for 'laptop'", { page: page2 });
// Extract from page1
const price1 = await stagehand.extract(
"get the first product price",
{ page: page1 }
);
// Extract from page2
const price2 = await stagehand.extract(
"get the first product price",
{ page: page2 }
);
console.log("Price comparison:", price1, "vs", price2);
Default Page Behavior
If you don’t specify a page, Stagehand uses the first page:
// These are equivalent:
await stagehand.act("click button");
await stagehand.act("click button", { page: stagehand.context.pages()[0] });
Multi-Tab Workflows
Example: Price Comparison
import { Stagehand } from "@browserbasehq/stagehand";
import { z } from "zod";
const stagehand = new Stagehand({ env: "LOCAL" });
await stagehand.init();
// Define schema
const productSchema = z.object({
title: z.string(),
price: z.number(),
});
// Open multiple store pages
const stores = [
"https://store1.com",
"https://store2.com",
"https://store3.com",
];
const pages = await Promise.all(
stores.map(async (url) => {
const page = await stagehand.context.newPage();
await page.goto(url);
return page;
})
);
// Search on all pages
await Promise.all(
pages.map((page) =>
stagehand.act("search for 'wireless mouse'", { page })
)
);
// Extract prices from all pages
const results = await Promise.all(
pages.map((page) =>
stagehand.extract(
"get the first product's title and price",
{ page, schema: productSchema }
)
)
);
// Find best price
const bestDeal = results.reduce((best, current) =>
current.extraction.price < best.extraction.price ? current : best
);
console.log("Best deal:", bestDeal.extraction);
await stagehand.close();
Example: Form Filling Across Tabs
const page1 = stagehand.context.pages()[0];
const page2 = await stagehand.context.newPage();
await page1.goto("https://site1.com/signup");
await page2.goto("https://site2.com/signup");
const userData = {
name: "John Doe",
email: "john@example.com",
};
// Fill forms in parallel
await Promise.all([
stagehand.act(
`type '${userData.name}' in name field and '${userData.email}' in email field`,
{ page: page1 }
),
stagehand.act(
`type '${userData.name}' in name field and '${userData.email}' in email field`,
{ page: page2 }
),
]);
Switching Between Tabs
Navigate Between Pages
const pages = stagehand.context.pages();
// Work on first tab
await stagehand.act("click button", { page: pages[0] });
// Switch to second tab
await stagehand.act("fill form", { page: pages[1] });
// Switch back to first tab
await stagehand.act("submit form", { page: pages[0] });
Bring Page to Front
const page = await stagehand.context.newPage();
await page.goto("https://example.com");
// Bring this page to front (useful in headed mode)
await page.bringToFront();
Handling Pop-ups and New Windows
Wait for New Pages
const page = stagehand.context.pages()[0];
await page.goto("https://example.com");
// Listen for new page
const newPagePromise = new Promise((resolve) => {
stagehand.context.on("page", resolve);
});
// Click something that opens a new tab
await stagehand.act("click the 'Open in New Tab' link", { page });
// Wait for the new page
const newPage = await newPagePromise;
console.log("New page opened:", newPage.url());
// Work with the new page
await stagehand.act("perform action", { page: newPage });
Handle Target=“_blank” Links
const page = stagehand.context.pages()[0];
await page.goto("https://example.com");
const pagesBefore = stagehand.context.pages().length;
// Click link that opens in new tab
await stagehand.act("click the 'Terms' link", { page });
// Wait a moment for new page to open
await page.waitForTimeout(1000);
const pagesAfter = stagehand.context.pages();
if (pagesAfter.length > pagesBefore) {
const newPage = pagesAfter[pagesAfter.length - 1];
console.log("New tab opened:", newPage.url());
}
Closing Pages
Close Specific Page
const page = await stagehand.context.newPage();
await page.goto("https://example.com");
// Do work...
// Close this specific page
await page.close();
Close All But One
const pages = stagehand.context.pages();
const mainPage = pages[0];
// Close all other pages
for (let i = 1; i < pages.length; i++) {
await pages[i].close();
}
Close All Pages
// Close entire browser (closes all pages)
await stagehand.close();
Parallel Execution
Execute on Multiple Pages Concurrently
const urls = [
"https://site1.com",
"https://site2.com",
"https://site3.com",
];
// Create pages in parallel
const pages = await Promise.all(
urls.map(async (url) => {
const page = await stagehand.context.newPage();
await page.goto(url);
return page;
})
);
// Extract data from all pages in parallel
const results = await Promise.all(
pages.map((page) =>
stagehand.extract("get page title", { page })
)
);
console.log("Results:", results);
Rate Limiting
async function processUrlsWithLimit(
stagehand: Stagehand,
urls: string[],
concurrency: number
) {
const results = [];
for (let i = 0; i < urls.length; i += concurrency) {
const batch = urls.slice(i, i + concurrency);
const batchResults = await Promise.all(
batch.map(async (url) => {
const page = await stagehand.context.newPage();
await page.goto(url);
const result = await stagehand.extract("get data", { page });
await page.close();
return result;
})
);
results.push(...batchResults);
}
return results;
}
// Process 20 URLs, 5 at a time
const urls = Array.from({ length: 20 }, (_, i) => `https://site${i}.com`);
const results = await processUrlsWithLimit(stagehand, urls, 5);
Agent with Multiple Pages
Specify Page for Agent
const page1 = stagehand.context.pages()[0];
const page2 = await stagehand.context.newPage();
await page1.goto("https://store.com");
await page2.goto("https://comparison.com");
const agent = stagehand.agent();
// Agent works on page1
const result1 = await agent.execute({
instruction: "find the cheapest laptop",
maxSteps: 10,
page: page1,
});
// Agent works on page2
const result2 = await agent.execute({
instruction: "find the cheapest laptop",
maxSteps: 10,
page: page2,
});
Multi-Page Agent Workflow
const agent = stagehand.agent();
// Start on first page
const page1 = stagehand.context.pages()[0];
await page1.goto("https://email-provider.com");
// Agent logs in and gets verification code
const emailResult = await agent.execute({
instruction: "log in and extract the verification code from the latest email",
maxSteps: 10,
page: page1,
});
const code = emailResult.extraction.code;
// Switch to second page for actual signup
const page2 = await stagehand.context.newPage();
await page2.goto("https://service.com/signup");
// Agent completes signup with verification code
await agent.execute({
instruction: `complete signup and enter verification code '${code}'`,
maxSteps: 10,
page: page2,
});
Managing Page State
Check Page Count
const pageCount = stagehand.context.pages().length;
console.log(`Currently managing ${pageCount} pages`);
if (pageCount > 10) {
console.warn("Too many pages open, consider closing some");
}
Track Pages
const pageMap = new Map<string, any>();
const page1 = await stagehand.context.newPage();
await page1.goto("https://store.com");
pageMap.set("store", page1);
const page2 = await stagehand.context.newPage();
await page2.goto("https://comparison.com");
pageMap.set("comparison", page2);
// Later, retrieve specific pages
const storePage = pageMap.get("store");
await stagehand.act("search for product", { page: storePage });
Best Practices
Close unused pages
Prevent memory leaks by closing pages you no longer need
Specify page explicitly
Always pass { page } to avoid confusion with multiple tabs
Use parallel execution
Process multiple pages concurrently for better performance
Handle page creation events
Listen for new pages when clicking links that open new tabs
Limit concurrency
Don’t open too many pages at once (causes performance issues)
Track page purpose
Use a Map or object to track which page is which
Common Patterns
Data Aggregation
const sources = [
"https://news1.com",
"https://news2.com",
"https://news3.com",
];
const pages = await Promise.all(
sources.map(async (url) => {
const page = await stagehand.context.newPage();
await page.goto(url);
return page;
})
);
const headlines = await Promise.all(
pages.map((page) =>
stagehand.extract("get top 5 headlines", { page })
)
);
const allHeadlines = headlines.flatMap(h => h.extraction);
Sequential Processing
const tasks = [
{ url: "https://site1.com", action: "extract data" },
{ url: "https://site2.com", action: "extract data" },
{ url: "https://site3.com", action: "extract data" },
];
for (const task of tasks) {
const page = await stagehand.context.newPage();
await page.goto(task.url);
const result = await stagehand.extract(task.action, { page });
console.log(`Result from ${task.url}:`, result);
await page.close();
}
Troubleshooting
Page Not Responding
try {
await stagehand.act("click button", {
page,
timeout: 10_000,
});
} catch (error) {
console.log("Page may have crashed or become unresponsive");
// Close and recreate page
await page.close();
page = await stagehand.context.newPage();
await page.goto(url);
}
Memory Management
// Process large number of URLs
for (let i = 0; i < urls.length; i++) {
const page = await stagehand.context.newPage();
await page.goto(urls[i]);
const result = await stagehand.extract("get data", { page });
results.push(result);
// Close page after each iteration
await page.close();
}
Related