state object. Variables, pages, and listeners persist between calls. Browser tabs are shared, but state is not.123456789playwriter session new # => 1 playwriter session new # => 2 # List all sessions with their stored keys playwriter session list # ID State Keys # -------------- # 1 myPage, userData # 2 -
-s <id> to all commands to use a specific session:12playwriter -s 1 -e "state.users = ['Alice', 'Bob']" playwriter -s 2 -e "console.log(state.users)" # undefined (isolated)
state object persists between execute calls within the same session. Use it to store pages, data, listeners, and anything else you need across calls:12345678// Call 1: store data state.page = context.pages().find(p => p.url() === 'about:blank') ?? await context.newPage() await state.page.goto('https://example.com') state.results = [] // Call 2: data is still there console.log(state.results.length) // 0 state.results.push(await state.page.title())
context.pages() returns all browser tabs with Playwriter enabled, shared across all sessions. Multiple agents see the same tabs. To avoid interference, always get your own page:1234// First call: grab an empty tab or create one, navigate immediately state.page = context.pages().find(p => p.url() === 'about:blank') ?? await context.newPage() await state.page.goto('https://example.com') // Store in state.page and use it for ALL subsequent operations
about:blank tab between execute calls.1234if (!state.page || state.page.isClosed()) { state.page = context.pages().find(p => p.url() === 'about:blank') ?? await context.newPage() } await state.page.goto('https://example.com')
context.pages() if the user explicitly asks you to control a specific tab:123const pages = context.pages().filter(x => x.url().includes('myapp.com')) if (pages.length === 0) throw new Error('No myapp.com page found') state.targetPage = pages[0]
12345678# Reset a session (reconnect browser, clear state) playwriter session reset 1 # Delete a session playwriter session delete 1 # List all sessions playwriter session list
| Variable | Description |
state | Persisted object, isolated per session |
page | Default page (shared, prefer state.page) |
context | Browser context, access all pages via context.pages() |
require | Load Node.js modules (path, fs, crypto, etc.) |
fetch | Standard fetch API |
Buffer, URL, URLSearchParams | Standard globals |
setTimeout, setInterval | Timers |
crypto, process | Node.js globals |
window.open, OAuth login flows, target=_blank) are auto-relocated to tabs in the main window by the extension. The new tab appears in context.pages():1234567await state.page.locator('button:has-text("Login with Google")').click() await state.page.waitForTimeout(1000) // New tab is the last page const pages = context.pages() const loginPage = pages[pages.length - 1] await loginPage.locator('[data-email]').first().click()
[WARNING] New page opened from current page (index N, initial url: ...) message pointing to the new tab.1state.page.removeAllListeners()
browser.close() or context.close(). Only close pages you created yourself.