Skip to main content

Overview

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 traversal
const button = page.deepLocator("iframe#myframe >> button.submit");
await button.click();

Syntax

page.deepLocator()

Create a deep locator that can cross iframe and shadow DOM boundaries.
page.deepLocator(selector: string): DeepLocatorDelegate
selector
string
required
Selector string with optional iframe hop notation (>>).Supports:
  • CSS selectors - Standard CSS syntax
  • XPath - Prefix with xpath= or start with /
  • Hop notation - Use >> to traverse into iframes
  • Deep XPath - Automatically handles iframe steps in XPath
Returns: DeepLocatorDelegate - A locator-like object that resolves frames on each action.

Hop Notation

The >> operator allows you to traverse into iframes in a readable way:
// Syntax: parent-selector >> child-selector >> target-selector
page.deepLocator("iframe#outer >> iframe.inner >> button")
Each segment before >> represents an iframe to traverse into. The final segment is the target element.

Examples

// Single iframe hop
page.deepLocator("iframe#payment >> input#card-number")

// Multiple iframe hops
page.deepLocator("iframe#level1 >> iframe#level2 >> div.content")

// XPath with hops
page.deepLocator("//iframe[@id='myframe'] >> //button[@class='submit']")

// CSS with XPath target
page.deepLocator("iframe.widget >> xpath=//div[@data-id='123']")

Deep XPath

When using XPath, deepLocator automatically recognizes iframe steps and traverses into them:
// Automatically traverses into iframes
page.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.

Methods

DeepLocatorDelegate provides the same API as Locator, with automatic frame resolution:

Interaction Methods

All interaction methods from Locator are available:
  • click(options?) - Click the element
  • fill(value) - Fill an input
  • type(text, options?) - Type text
  • hover() - Hover over element
  • selectOption(values) - Select dropdown options
  • scrollTo(percent) - Scroll element

State Methods

  • isVisible() - Check visibility
  • isChecked() - Check checkbox state
  • inputValue() - Get input value
  • textContent() - Get text content
  • innerText() - Get visible text
  • innerHtml() - Get HTML content

Selection Methods

  • count() - Count matching elements
  • nth(index) - Select by index
  • first() - Get first element

Utility Methods

  • highlight(options?) - Highlight element
  • centroid() - Get center coordinates
  • backendNodeId() - Get DOM node ID
  • sendClickEvent(options?) - Dispatch click event
All methods work identically to Locator, but automatically resolve the correct frame before executing.

Code Examples

  • Basic Iframe Traversal
  • Multiple Iframes
  • Deep XPath
  • Element Selection
  • Payment Forms
  • State Checks
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 environment
const stagehand = new Stagehand({ env: "BROWSERBASE" });
await stagehand.init();
const page = stagehand.context.pages()[0];

await page.goto("https://example.com");

// Click button inside iframe
const button = page.deepLocator("iframe#widget >> button.submit");
await button.click();

// Fill input in nested iframe
const input = page.deepLocator("iframe#outer >> iframe#inner >> input#email");
await input.fill("user@example.com");

await stagehand.close();

Comparison with Standard Locator

Standard Locator (Single Frame)

// Only works in the main frame
const button = page.locator("button.submit");
await button.click();

// Cannot access elements inside iframes
const iframeButton = page.locator("iframe >> button"); // ❌ Won't work

Deep Locator (Cross-Frame)

// Works across iframe boundaries
const button = page.deepLocator("iframe#widget >> button.submit");
await button.click(); // ✅ Automatically traverses into iframe

// Can handle nested iframes
const nested = page.deepLocator("iframe#a >> iframe#b >> button");
await nested.click(); // ✅ Handles multiple levels

When to Use deepLocator

Use deepLocator() when:
  1. Targeting elements inside iframes - Payment forms, embedded widgets, third-party content
  2. Working with nested iframes - Multiple levels of iframe nesting
  3. XPath crosses iframe boundaries - When XPath naturally includes iframe steps
  4. Simpler syntax preferred - Use >> instead of manual frame switching
Use standard locator() when:
  1. Elements are in main frame - No iframe traversal needed
  2. Performance critical - Standard locator is slightly faster (no frame resolution)
  3. Working with frame references - You already have the frame object

Best Practices

  1. Use specific selectors - Make each segment unique to avoid ambiguity
  2. Keep hop chains short - Simpler is better for maintainability
  3. Name your iframes - Use IDs or classes on iframes for easier targeting
  4. Test incrementally - Verify each segment works before adding more
  5. Cache selectors - Store complex selectors in variables for reuse
  6. Use highlight() for debugging - Verify you’re targeting the right element

Common Patterns

Named Iframe References

// Define iframe selectors
const PAYMENT_FRAME = "iframe#stripe-payment";
const WIDGET_FRAME = "iframe.embedded-widget";

// Use in deep locators
await page.deepLocator(`${PAYMENT_FRAME} >> input#card`).fill("4242");
await page.deepLocator(`${WIDGET_FRAME} >> button`).click();

Conditional Iframe Interaction

const errorInIframe = page.deepLocator("iframe#form >> .error-message");
if (await errorInIframe.isVisible()) {
  const errorText = await errorInIframe.textContent();
  console.error("Form error:", errorText);
}

Dynamic Frame Selection

// Select iframe by attribute
const frameSelector = `iframe[data-widget-id="${widgetId}"]`;
const button = page.deepLocator(`${frameSelector} >> button.action`);
await button.click();

Error Handling

Deep locator operations may throw:
  • Element not found - Selector doesn’t match in the target frame
  • Frame not found - Iframe selector doesn’t resolve
  • Timeout errors - Frame or element resolution timed out
  • Invalid selector - Malformed selector syntax
Handle errors appropriately:
try {
  await page.deepLocator("iframe#widget >> button").click();
} catch (error) {
  console.error("Deep locator failed:", error.message);
  // Fallback or retry logic
}

Advanced Usage

Combining with Page Methods

// Navigate then use deep locator
await page.goto("https://example.com");
await page.waitForLoadState("networkidle");

const iframeButton = page.deepLocator("iframe#app >> button");
await iframeButton.click();

With AI-Powered Methods

// Use observe to find elements in iframes
const actions = await stagehand.observe("find buttons in the payment iframe");

// Then use deep locator for precise interaction
await page.deepLocator("iframe#payment >> button.submit").click();

Technical Details

How It Works

  1. Parse selector - Splits on >> or parses XPath for iframe steps
  2. Build frame chain - Creates FrameLocator chain for each iframe segment
  3. Resolve final frame - Navigates through frames to find target frame
  4. Create locator - Returns a locator in the correct frame context
  5. Lazy execution - Frame resolution happens fresh on each action

Frame Resolution

Deep locators use the internal FrameLocator and resolveLocatorWithHops logic to:
  • Track frame hierarchies
  • Handle OOPIF (out-of-process iframes)
  • Support shadow DOM piercing
  • Maintain frame references during navigation

Type Definitions

interface DeepLocatorDelegate {
  // Actions
  click(options?: { button?: MouseButton; clickCount?: number }): Promise<void>;
  fill(value: string): Promise<void>;
  type(text: string, options?: { delay?: number }): Promise<void>;
  hover(): Promise<void>;
  selectOption(values: string | string[]): Promise<string[]>;
  scrollTo(percent: number | string): Promise<void>;

  // State
  isVisible(): Promise<boolean>;
  isChecked(): Promise<boolean>;
  inputValue(): Promise<string>;
  textContent(): Promise<string>;
  innerText(): Promise<string>;
  innerHtml(): Promise<string>;

  // Selection
  count(): Promise<number>;
  nth(index: number): DeepLocatorDelegate;
  first(): DeepLocatorDelegate;

  // Utilities
  highlight(options?: HighlightOptions): Promise<void>;
  centroid(): Promise<{ x: number; y: number }>;
  backendNodeId(): Promise<BackendNodeId>;
  sendClickEvent(options?: EventOptions): Promise<void>;
}