> Agent-readable docs index: /llms.txt. Download /docs.zip to grep all markdown files locally.

---
title: Accessibility Snapshots, Search, and Page Diffing
sidebarTitle: Snapshots
description: Read page state as structured text instead of screenshots, search for elements, and track DOM changes with automatic diffing between calls.
icon: lucide:scan-text
---

Accessibility snapshots are the **primary way** agents read pages. They return every interactive element as text with Playwright locators attached. **5-20KB instead of 100KB+** for a screenshot; cheaper, faster, and parseable without vision.

## Basic usage

```js
// Full page snapshot
await snapshot({ page: state.page })

// Output:
// - banner:
//     - link "Home" [id="nav-home"]
//     - navigation:
//         - link "Docs" [data-testid="docs-link"]
//         - link "Blog" role=link[name="Blog"]
```

Each interactive line ends with a **Playwright locator** you can pass directly to `state.page.locator()`. If multiple elements share the same locator, a `>> nth=N` suffix is added to make it unique.

## Options

| Parameter               | Type             | Default  | Description                                    |
| ----------------------- | ---------------- | -------- | ---------------------------------------------- |
| `page`                  | Page             | required | Playwright page to snapshot                    |
| `frame`                 | Frame            | -        | Snapshot a specific iframe instead             |
| `locator`               | Locator          | -        | Scope snapshot to a subtree                    |
| `search`                | string \| RegExp | -        | Filter results (first 10 matches with context) |
| `showDiffSinceLastCall` | boolean          | `true`   | Return diff since last snapshot                |
| `interactiveOnly`       | boolean          | `false`  | Only show interactive elements                 |
| `format`                | string           | -        | Output format                                  |

## Search

Filter large pages to find specific elements:

```js
// Search by string or regex
await snapshot({ page: state.page, search: /button|submit/i })

// Search for obstacles after navigation
await snapshot({ page: state.page, search: /cookie|consent|accept|login/i })
```

Search returns the **first 10 matching lines** with surrounding context. When `search` is provided, diffing is disabled by default so the search filters the full content.

## Automatic diffing

Snapshots return **full content on first call**, then **diffs on subsequent calls**. This saves tokens by only showing what changed.

```js
// First call: returns full snapshot
await snapshot({ page: state.page })

// After an action: returns only changes (+ added, - removed)
await state.page.locator('button').click()
await snapshot({ page: state.page })
// Output:
// - button "Submit"
// + button "Submit" [disabled]
// + status "Sending..."
```

The diff is only returned when it's **shorter than the full content**. If nothing changed, you get: `"No changes since last snapshot"`.

**Control diffing explicitly:**

```js
// Always get full content (disable diff)
await snapshot({ page: state.page, showDiffSinceLastCall: false })

// Combine search + diff (both disabled by default, enable explicitly)
await snapshot({ page: state.page, search: /error/i, showDiffSinceLastCall: true })
```

## Scoping to elements

Scope a snapshot to a specific part of the page to **dramatically reduce output size**:

```js
// Full page: ~150 lines (sidebar, nav, header, footer, everything)
await snapshot({ page: state.page })

// Scoped to main: ~20 lines (just the content area)
await snapshot({ locator: state.page.locator('main') })

// Scope to a dialog, form, or section
await snapshot({ locator: state.page.locator('[role="dialog"]') })
await snapshot({ locator: state.page.locator('form#checkout') })
```

## Iframe snapshots

Snapshot iframe content using `contentFrame()`:

```js
const frame = await state.page.locator('iframe').contentFrame()
await snapshot({ frame })
```

## Using locators from snapshots

Locators from snapshots map directly to Playwright:

```js
// Snapshot shows: role=radio[name="Nope, Vanilla"]
await state.page.getByRole('radio', { name: 'Nope, Vanilla' }).click()

// Snapshot shows: role=link[name="SIGN IN"]
await state.page.locator('role=link[name="SIGN IN"]').click()

// Snapshot shows: [data-testid="docs-link"]
await state.page.locator('[data-testid="docs-link"]').click()
```

**Never invent selectors.** The snapshot output IS the selector. Use it directly.

## Alternative content methods

### getCleanHTML

Get **cleaned HTML** from a locator or page. Automatically removes script/style/svg/head tags, unwraps empty wrappers, and keeps semantic attributes.

```js
await getCleanHTML({ locator: state.page.locator('body') })
await getCleanHTML({ locator: state.page, search: /button/i })
await getCleanHTML({ locator: state.page, showDiffSinceLastCall: false })
await getCleanHTML({ locator: state.page, includeStyles: true })  // keep class/style attrs
```

Supports the same **search** and **diffing** behavior as `snapshot()`.

### getPageMarkdown

Extract **main article content** as plain text using Mozilla Readability (same algorithm as Firefox Reader View). Strips navigation, ads, sidebars, and clutter.

```js
const content = await getPageMarkdown({ page: state.page })
// Output:
// # Article Title
// Author: John Doe | Site: example.com | Published: 2024-01-15
// > Article excerpt or description
// The main article content as plain text...
```

Also supports `search` and `showDiffSinceLastCall`.

## When to use which

| Method                                | Best for                                | Output                                                  |
| ------------------------------------- | --------------------------------------- | ------------------------------------------------------- |
| `snapshot()`                          | Interactive elements, forms, navigation | Accessibility tree with locators                        |
| `getCleanHTML()`                      | Raw HTML structure, style debugging     | Cleaned HTML                                            |
| `getPageMarkdown()`                   | Articles, blog posts, documentation     | Plain text with title/author                            |
| `screenshotWithAccessibilityLabels()` | Visual layouts, grids, dashboards       | Image + text (see [visual labels](/docs/visual-labels)) |
