Skip to main content

What is observe()?

await stagehand.observe("find the login button");
observe() discovers actionable elements on a page and returns structured actions you can execute or validate before acting. Use it to explore pages, plan multi-step workflows, cache actions, and validate elements before acting.

Why use observe()?

Using observe()

Use observe() to discover actionable elements on a page. Here’s how to find a button:
const page = stagehand.context.pages()[0];
await page.goto("https://example.com");
const actions = await stagehand.observe("find the learn more button");
iFrame and Shadow DOM Support Stagehand automatically handles iFrame traversal and shadow DOM elements without requiring additional configuration.
Use CaseExample instruction
Find buttonsfind the submit button
Locate formsfind all input fields in the form
Discover linksfind navigation links
Identify tablesfind the pricing table
Map workflowsfind all checkout steps
Validate elementsfind the delete account button

Return value of observe()?

When you use observe(), Stagehand will return a Promise<Action[]> with the following structure:
[
  {
    description: 'Learn more button',
    method: 'click',
    arguments: [],
    selector: 'xpath=/html[1]/body[1]/shadow-demo[1]//div[1]/button[1]'
  }
]
  • Do this
  • Don't do this
Use specific, descriptive instructions.
// Clear and specific
await stagehand.observe("find the primary call-to-action button in the hero section");
await stagehand.observe("find all input fields in the checkout form");
await stagehand.observe("find the delete account button in settings");

Advanced Configuration

You can pass additional options to configure the model, timeout, and selector scope:
// Custom model configuration
const actions = await stagehand.observe("find navigation links", {
  model: "openai/gpt-4o",
  timeout: 30000,
  selector: "//header" // Focus on specific area
});

Using with Custom Pages

You can use observe() with pages from other browser automation libraries like Puppeteer, Playwright, or Patchright by passing the page option:
import { Stagehand } from "@browserbasehq/stagehand";
import puppeteer from "puppeteer-core";

const stagehand = new Stagehand({
  env: "BROWSERBASE",
});
await stagehand.init();

// Connect with Puppeteer
const browser = await puppeteer.connect({
  browserWSEndpoint: stagehand.connectURL(),
  defaultViewport: null,
});

const pages = await browser.pages();
const customPage = pages[0];

await customPage.goto("https://www.example.com/products");

// Use observe with the custom Puppeteer page
const actions = await stagehand.observe("find all product cards", {
  page: customPage
});
This works with:
  • Puppeteer: Pass Puppeteer Page objects
  • Playwright: Pass Playwright Page objects
  • Patchright: Pass Patchright Page objects
  • Stagehand Context Pages: Access pages via stagehand.context.pages() (default)

Complete API Reference

See the full observe() reference for detailed parameter documentation, return values, and advanced examples.

Best practices

Plan then execute

Discover all actions once, then execute without additional LLM calls. This approach is 2-3x faster than separate act() calls.
const formFields = await stagehand.observe("find all form input fields");

for (const field of formFields) {
  await stagehand.act(field); // No LLM call
}

Analyze pages with observe()

Complete guide to planning actions with observe().

Scope extractions

Use observe() to narrow extraction scope and reduce token usage by up to 10x.
const [table] = await stagehand.observe("find the pricing table");

const pricing = await stagehand.extract({
  instruction: "extract all pricing tiers",
  schema: PricingSchema,
  selector: table.selector
});

Extract structured data

Learn how to use observe() with extract() for precise data extraction.

Validate before acting

Check elements exist and verify their properties before performing critical operations.
const [deleteButton] = await stagehand.observe("find the delete account button");

if (deleteButton?.method === "click") {
  await stagehand.act(deleteButton);
} else {
  throw new Error("Delete button not found");
}

Execute actions with act()

Learn how to execute observed actions reliably.

Cache observed actions

Store and reuse observed actions to eliminate redundant LLM calls. Build a simple cache:
const actionCache = new Map<string, Action[]>();

async function cachedObserve(instruction: string) {
  if (actionCache.has(instruction)) {
    return actionCache.get(instruction)!;
  }

  const actions = await stagehand.observe(instruction);
  actionCache.set(instruction, actions);
  return actions;
}

Complete caching guide

Learn advanced caching techniques and patterns for optimal performance.

Troubleshooting

Problem: observe() returns empty arraySolutions:
  • Verify the element exists on the page
  • Use more specific instructions (e.g., “find the blue submit button” instead of “find button”)
  • Ensure page has fully loaded before calling observe()
  • Enable verbose logging in Stagehand configuration to inspect detection behavior
// Check page state before observing
const page = stagehand.context.pages()[0];
await page.waitForLoadState('domcontentloaded');

const actions = await stagehand.observe("find the submit button");

if (actions.length === 0) {
  console.log("No elements found, trying alternative instruction");
  const altActions = await stagehand.observe("find the button at the bottom of the form");
}
Problem: Descriptions or selectors don’t match actual elementsSolutions:
  • Use more capable models—check model evals for recommendations
  • Provide more context in your instruction (e.g., “find the submit button in the checkout form”)
  • Enable verbose logging and logInferenceToFile in Stagehand configuration to inspect LLM reasoning
// More specific instructions improve accuracy
// Instead of:
await stagehand.observe("find the button");

// Use context:
await stagehand.observe("find the red 'Delete' button in the user settings panel");
Problem: The method field has an unexpected valueSolutions:
  • Validate the method before using it: if (action.method === "click") { ... }
  • Check supported actions for valid method names
  • Override with a specific method when needed: await stagehand.act({ ...action, method: "click" })
const [action] = await stagehand.observe("find the submit button");

// Validate method before acting
const validMethods = ["click", "fill", "type", "press"];
if (action && validMethods.includes(action.method || "")) {
  await stagehand.act(action);
} else {
  console.warn(`Unexpected method: ${action?.method}`);
}

Next steps