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

Accessibility Snapshots, Search, and Page Diffing

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

// 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

ParameterTypeDefaultDescription
pagePagerequiredPlaywright page to snapshot
frameFrame-Snapshot a specific iframe instead
locatorLocator-Scope snapshot to a subtree
searchstring | RegExp-Filter results (first 10 matches with context)
showDiffSinceLastCallbooleantrueReturn diff since last snapshot
interactiveOnlybooleanfalseOnly show interactive elements
formatstring-Output format

Search

Filter large pages to find specific elements:
// 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.
// 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:
// 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:
// 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():
const frame = await state.page.locator('iframe').contentFrame() await snapshot({ frame })

Using locators from snapshots

Locators from snapshots map directly to Playwright:
// 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.
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.
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

MethodBest forOutput
snapshot()Interactive elements, forms, navigationAccessibility tree with locators
getCleanHTML()Raw HTML structure, style debuggingCleaned HTML
getPageMarkdown()Articles, blog posts, documentationPlain text with title/author
screenshotWithAccessibilityLabels()Visual layouts, grids, dashboardsImage + text (see visual labels)