The deepLocator() method creates a special locator that can traverse iframe boundaries and shadow DOM using a simplified syntax. It automatically resolves the correct frame for each operation, making cross-frame interactions seamless.Access via the page object:
const stagehand = new Stagehand({ env: "BROWSERBASE" });await stagehand.init();const page = stagehand.context.pages()[0];// Deep locator with iframe traversalconst button = page.deepLocator("iframe#myframe >> button.submit");await button.click();
When using XPath, deepLocator automatically recognizes iframe steps and traverses into them:
// Automatically traverses into iframespage.deepLocator("//iframe//button")page.deepLocator("//iframe[@id='myframe']//input[@name='email']")page.deepLocator("//iframe[1]//iframe[2]//div[@class='target']")
The locator intelligently parses the XPath, identifies iframe boundaries, and resolves the correct frame for the final selector.
import { Stagehand } from "@browserbasehq/stagehand";// Initialize with Browserbase (API key and project ID from environment variables)// Set BROWSERBASE_API_KEY and BROWSERBASE_PROJECT_ID in your environmentconst stagehand = new Stagehand({ env: "BROWSERBASE" });await stagehand.init();const page = stagehand.context.pages()[0];await page.goto("https://example.com");// Click button inside iframeconst button = page.deepLocator("iframe#widget >> button.submit");await button.click();// Fill input in nested iframeconst input = page.deepLocator("iframe#outer >> iframe#inner >> input#email");await input.fill("user@example.com");await stagehand.close();
// Three-level iframe nestingawait page.deepLocator( "iframe#level1 >> iframe#level2 >> iframe#level3 >> div.target").click();// Different selectors at each levelawait page.deepLocator( "iframe.container >> #payment-frame >> input[name=cardNumber]").fill("4111111111111111");// Mixed CSS and XPathawait page.deepLocator( "iframe.widget >> xpath=//button[contains(text(), 'Submit')]").click();
// Simple iframe traversal with XPathconst content = page.deepLocator("//iframe//div[@class='content']");const text = await content.textContent();// Multiple iframe levelsconst button = page.deepLocator( "//iframe[@id='outer']//iframe[@class='inner']//button");await button.click();// XPath with predicatesconst input = page.deepLocator( "//iframe[1]//form[@id='myform']//input[@type='text'][1]");await input.fill("test value");
// Count elements across iframesconst buttons = page.deepLocator("iframe#widget >> button");const count = await buttons.count();console.log(`Found ${count} buttons in iframe`);// Select specific elementconst firstButton = buttons.first();await firstButton.click();const thirdButton = buttons.nth(2);await thirdButton.click();// Get text from all elementsfor (let i = 0; i < count; i++) { const btn = buttons.nth(i); const text = await btn.innerText(); console.log(`Button ${i}:`, text);}
// Only works in the main frameconst button = page.locator("button.submit");await button.click();// Cannot access elements inside iframesconst iframeButton = page.locator("iframe >> button"); // ❌ Won't work
// Navigate then use deep locatorawait page.goto("https://example.com");await page.waitForLoadState("networkidle");const iframeButton = page.deepLocator("iframe#app >> button");await iframeButton.click();
// Use observe to find elements in iframesconst actions = await stagehand.observe("find buttons in the payment iframe");// Then use deep locator for precise interactionawait page.deepLocator("iframe#payment >> button.submit").click();