> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stagehand.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Act

> Interact with a web page

export const V3Banner = () => null;

<V3Banner />

## What is `act()`?

```typescript theme={null}
await stagehand.act("click on add to cart")
```

`act` enables Stagehand to perform **individual** actions on a web page. Use it to build self-healing and deterministic automations that adapt to website changes.

## Why use `act()`?

<CardGroup cols={2}>
  <Card title="Natural Language Instructions" icon="wand-magic-sparkles" href="#using-act">
    Write automation in plain English. No selectors or complex syntax.
  </Card>

  <Card title="Precise Control" icon="crosshairs" href="#best-practices">
    Build automations step by step. Define exactly what happens at every moment.
  </Card>

  <Card title="Self-Healing" icon="bandage" href="#ensure-reliable-actions">
    Actions automatically adapt when websites change.
  </Card>

  <Card title="Caching" icon="repeat" href="#reduce-model-costs">
    Cache actions to avoid LLM calls and ensure consistent execution across runs.
  </Card>
</CardGroup>

## Using `act()`

Use `act` to perform single actions in your automation. Here's how to click a button:

```typescript theme={null}
await page.goto("https://example-store.com");
await stagehand.act("click the add to cart button");
```

<Note>
  **iFrame and Shadow DOM Support** Stagehand automatically handles iFrame traversal and shadow DOM elements without requiring additional configuration.
</Note>

With `act`, breaking complex actions into small, single-step actions works best. If you need to orchestrate multi-step flows, use multiple `act` commands or `agent`.

<Accordion title="Suggested actions">
  | Action               | Example instruction                |
  | -------------------- | ---------------------------------- |
  | Click                | `click the button`                 |
  | Fill                 | `fill the field with <value>`      |
  | Type                 | `type <text> into the search box`  |
  | Press                | `press <key> in the search field`  |
  | Scroll               | `scroll to <position>`             |
  | Select from dropdown | `select <value> from the dropdown` |
</Accordion>

### Return value of `act()`?

When you use `act()`, Stagehand will return a `Promise<ActResult>` with the following structure:

```typescript theme={null}
{
  success: true,
  message: 'Action [click] performed successfully on selector: xpath=/html[1]/body[1]/div[1]/span[1] → Action [click] performed successfully on selector: xpath=/html[1]/body[1]/div[2]/div[1]/section[1]/div[1]/div[1]/div[25]',
  actionDescription: 'Favorite Colour',
  actions: [
    {
      selector: 'xpath=/html[1]/body[1]/div[1]/span[1]',
      description: 'Favorite Colour',
      method: 'click',
      arguments: []
    },
    {
      selector: 'xpath=/html[1]/body[1]/div[2]/div[1]/section[1]/div[1]/div[1]/div[25]',
      description: 'Peach',
      method: 'click',
      arguments: []
    }
  ]
}
```

<Tabs>
  <Tab title="Do this" icon="check">
    Break your task into single-step actions.

    ```typescript theme={null}
    // Break it into single-step actions
    await stagehand.act("open the filters panel");
    await stagehand.act("choose 4-star rating");
    await stagehand.act("click the apply button");
    ```
  </Tab>

  <Tab title="Don't do this" icon="xmark">
    For multi-step tasks, use [`agent()`](/basics/agent) instead.

    ```typescript theme={null}
    // Too complex - trying to do multiple things at once
    await stagehand.act("open the filters panel, choose 4-star rating, and click apply");
    ```
  </Tab>
</Tabs>

## Advanced Configuration

You can pass additional options to configure the model, timeout, variables, and target page:

```typescript theme={null}
// Custom model configuration
await stagehand.act("choose 'Peach' from the favorite color dropdown", {
  model: "google/gemini-2.5-flash",
  timeout: 10000
});
```

### Server-side Caching

<Note>
  `serverCache` only works when running with `env: "BROWSERBASE"`. It has no effect in local environments.
</Note>

When running on Browserbase, Stagehand automatically caches `act()` results server-side. Repeated calls with the same inputs return instantly without consuming LLM tokens. Caching is enabled by default and can be controlled globally on the constructor or overridden per call:

```typescript theme={null}
// Disable server-side caching for the entire instance
const stagehand = new Stagehand({
  env: "BROWSERBASE",
  serverCache: false,
});

// Or disable it for a single call
await stagehand.act("click the login button", { serverCache: false });

// Check whether a result was served from cache
const result = await stagehand.act("click the login button");
console.log(result.cacheStatus); // "HIT", "MISS", or undefined
```

### Using with Custom Pages

You can use `act()` with pages from other browser automation libraries like Puppeteer, Playwright, or Patchright by passing the `page` option:

```typescript theme={null}
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/blog");

// Use act with the custom Puppeteer page
await stagehand.act("click the next page button", {
  page: customPage
});
```

This works with:

* **Puppeteer**: Pass Puppeteer Page objects
* **Playwright**: Pass Playwright Page objects
* **Patchright**: Pass Patchright Page objects
* **Stagehand Page**: Use `stagehand.context.pages()[0]` or `context.activePage()` (default)

<Card title="Complete API Reference" icon="book" href="/v3/references/act">
  See the full `act()` reference for detailed parameter documentation, return values, and advanced examples.
</Card>

## Best practices

### Ensure reliable actions

Use `observe()` to discover candidate actions on the current page and plan reliably. It returns a list of suggested actions (with selector, description, method, and arguments). You can pass an observed action directly to `act` to execute it.

```typescript theme={null}
const [action] = await stagehand.observe("click the login button");

if (action) {
  await stagehand.act(action);
}
```

<Card title="Analyze pages with observe()" icon="magnifying-glass" iconType="sharp-solid" href="/v3/basics/observe">
  Plan actions with `observe()` before executing with `act`.
</Card>

### Reduce model costs

Enable automatic action caching by specifying a `cacheDir` when initializing Stagehand. The first time an action runs, it's cached. Subsequent runs reuse the cached action without LLM calls.

```typescript theme={null}
import { Stagehand } from "@browserbasehq/stagehand";

// Enable caching by specifying a cache directory
const stagehand = new Stagehand({
  env: "BROWSERBASE",
  cacheDir: "act-cache" // Actions are automatically cached here
});

await stagehand.init();
const page = stagehand.context.pages()[0];

// First run - makes LLM call and caches the action
await stagehand.act("click the login button");
```

<Note>
  Caching persists across script executions. The first time you run your script, actions are cached to your local filesystem. On subsequent runs, cached actions are reused automatically, significantly reducing costs and improving performance.
</Note>

<Card title="Complete caching guide" icon="database" iconType="sharp-solid" href="/v3/best-practices/caching">
  Learn advanced caching techniques and patterns for optimal performance.
</Card>

### Secure your automations

Variables are **not shared with LLM providers**. Use them for passwords, API keys, and other sensitive data.

<Note>
  Load sensitive data from environment variables using `.env` files. Never hardcode API keys, passwords, or other secrets directly in your code.
</Note>

```typescript theme={null}
// Variables use %variableName% syntax in the instruction
await stagehand.act("type %username% into the email field", {
  variables: { username: "user@example.com" }
});

await stagehand.act("type %password% into the password field", {
  variables: { password: process.env.USER_PASSWORD }
});

await stagehand.act("click the login button");
```

<Warning>
  When handling sensitive data, set `verbose: 0` in your Stagehand configuration to prevent secrets from appearing in logs. See the [configuration guide](/configuration/browser) for more details.
</Warning>

<Card title="User Data Best Practices" icon="shield-check" iconType="sharp-solid" href="/v3/best-practices/user-data">
  Complete guide to securing your browser automations with best practices and configurations.
</Card>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Method not supported">
    **Problem**: `act` fails with "method not supported" error

    **Solutions**:

    * Use clear and detailed instructions for what you want to accomplish
    * Review our [evals](https://stagehand.dev/evals) to find the best models for your use case
    * Use [`observe()`](/basics/observe) and verify the resulting action is within a list of expected actions

    **Solution 1: Validate with observe**

    ```typescript theme={null}
    const prompt = "click the submit button";
    const expectedMethod = "click";

    try {
      await stagehand.act(prompt);
    } catch (error) {
      if (error.message.includes("method not supported")) {
        // Observe the same prompt to get the planned action
        const [action] = await stagehand.observe(prompt);

        if (action && action.method === expectedMethod) {
          await stagehand.act(action);
        } else {
          throw new Error(`Unsupported method: expected "${expectedMethod}", got "${action?.method}"`);
        }
      } else {
        throw error;
      }
    }
    ```

    **Solution 2: Retry with exponential backoff**

    ```typescript theme={null}
    // Retry with exponential backoff for intermittent issues
    const prompt = "click the submit button";
    const maxRetries = 3;

    for (let attempt = 0; attempt <= maxRetries; attempt++) {
      try {
        await stagehand.act(prompt, { timeout: 10000 + (attempt * 5000) });
        break; // Success, exit retry loop
      } catch (error) {
        if (error.message.includes("method not supported") && attempt < maxRetries) {
          // Exponential backoff: wait 2^attempt seconds
          const delay = Math.pow(2, attempt) * 1000;
          console.log(`Retry ${attempt + 1}/${maxRetries} after ${delay}ms`);
          await new Promise(resolve => setTimeout(resolve, delay));
        } else {
          throw error;
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="Action failed or timed out">
    **Problem**: `act` times out or fails to complete action (often due to element not found)

    **Solutions**:

    * Ensure page has fully loaded
    * Check if content is in iframes: [Learn more about working with iframes](/best-practices/working-with-iframes)
    * Increase action timeout
    * Use `observe()` first to verify element exists

    ```typescript theme={null}
    // Handle timeout and element not found issues
    try {
      await stagehand.act("click the submit button", { timeout: 30000 });
    } catch (error) {
      // Check if page is fully loaded
      await page.waitForLoadState('domcontentloaded');

      // Use observe to check element state
      const [element] = await stagehand.observe("find the submit button");

      if (element) {
        console.log("Element found, trying more specific instruction");
        await stagehand.act("click the submit button at the bottom of the form");
      } else {
        console.log("Element not found, trying alternative selector");
        await stagehand.act("click the button with text 'Submit'");
      }
    }
    ```
  </Accordion>

  <Accordion title="Incorrect element selected">
    **Problem**: `act` performs action on wrong element

    **Solutions**:

    * Be more specific in instructions: include visual cues, position, or context
    * Use `observe()` to preview which element will be selected
    * Add contextual information: "the search button in the header"
    * Use unique identifiers when available

    ```typescript theme={null}
    // More precise element targeting
    // Instead of:
    await stagehand.act("click the button");

    // Use specific context:
    await stagehand.act("click the red 'Delete' button next to the user John Smith");

    // Or preview with observe first:
    const [action] = await stagehand.observe("click the submit button in the checkout form");
    if (action.description.includes("checkout")) {
      await stagehand.act(action);
    }
    ```
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Orchestrate complex workflows with Agent" icon="robot" iconType="sharp-solid" href="/v3/basics/agent">
    Use `Agent` to autonomously execute multi-step tasks and complex workflows.
  </Card>

  <Card title="Caching actions" icon="bolt" iconType="sharp-solid" href="/v3/best-practices/caching">
    Speed up repeated automations by caching actions.
  </Card>

  <Card title="Extract data with extract()" icon="table" iconType="sharp-solid" href="/v3/basics/extract">
    Use `extract` with a data schema to pull clean, typed data from any page.
  </Card>

  <Card title="Preview actions with observe()" icon="magnifying-glass" iconType="sharp-solid" href="/v3/basics/observe">
    Preview actions with `observe()` before executing them.
  </Card>
</CardGroup>
