Playwright UI Mode: The Complete Interactive Testing Guide
If you have ever stared at a red CI log trying to guess why a Playwright test failed, you already know the pain: a stack trace tells you what threw, but never what the page looked like at that exact moment. Playwright UI Mode fixes that by giving you a fully interactive runner with time-travel debugging, a live DOM snapshot for every action, watch mode, and a built-in locator picker. In this Playwright UI mode guide you will learn how to launch it, read each panel, debug a flaky test by stepping backward through time, generate rock-solid locators, and inspect network traffic and console output without ever adding a single console.log.
🎠Want to master this with real projects? Join the Playwright Automation Mastery course at The Testing Academy.
Contents
What Is Playwright UI Mode?
UI Mode is a graphical test runner that ships inside the @playwright/test package, so there is nothing extra to install. It runs your existing spec files but wraps them in a browser-based dashboard where you can pick which tests to run, watch them execute action by action, and scrub through a recorded trace of the entire run. Unlike a plain npx playwright test invocation that just prints pass/fail, UI Mode keeps a DOM snapshot, network log, console log, and source mapping for every step.
Launch it from your project root with a single flag:
// From the terminal, in your project root
npx playwright test --ui
// Target a single file or a grep pattern
npx playwright test tests/checkout.spec.ts --ui
npx playwright test --ui --grep @smoke
// Pin a project from playwright.config.ts (e.g. only Chromium)
npx playwright test --ui --project=chromium
The first time you run it, a desktop window opens with your test tree on the left. Tick a test, hit the play icon, and Playwright executes it while recording everything. The window is itself a browser, so it works identically on macOS, Windows, and Linux without any OS-specific GUI dependencies.
Touring the UI Mode Panels
Before debugging anything, it helps to know what each region of the window does. Here is the map you will use every day:
| Panel | What it shows | When you reach for it |
|---|---|---|
| Test tree (left) | Files, describes, and individual tests with status icons | Selecting, filtering, and re-running tests |
| Actions list | Every step (goto, click, fill, expect) in execution order | Stepping through a run to find the failing action |
| Snapshot / timeline | Time-travel DOM screenshot for the selected action | Seeing exactly what the page looked like |
| Pick locator | Hover the snapshot to generate a recommended locator | Authoring stable selectors fast |
| Source tab | Your spec source with the current line highlighted | Mapping an action back to test code |
| Network / Console / Log | Requests, browser console output, and Playwright call log | Diagnosing API failures and JS errors |
| Attachments | Screenshots, videos, and downloaded files | Reviewing visual evidence after a run |
The single most important idea is the Actions list paired with the Snapshot panel. Click any action and the snapshot panel rewinds the page to that instant. That is the time-travel feature, and it is what makes UI Mode dramatically faster than reading logs.
Time-Travel Debugging in Practice
Consider a small login spec. Run it in UI Mode, then click through the actions to watch the DOM evolve.
import { test, expect } from '@playwright/test';
test('user can log in and reach the dashboard', async ({ page }) => {
await page.goto('https://example.com/login');
await page.getByLabel('Email').fill('jane@scrolltest.com');
await page.getByLabel('Password').fill('S3cret!');
await page.getByRole('button', { name: 'Sign in' }).click();
// UI Mode shows the DOM snapshot right before and after this assertion
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
await expect(page).toHaveURL(/.*\/dashboard/);
});
When you select the click action, UI Mode highlights the exact element the click targeted with a coloured overlay on the snapshot. Each action exposes two snapshots: a Before state and an After state. If the "Sign in" button moved or was covered by a cookie banner, the Before snapshot reveals it instantly. The Action tab also reports timing and the resolved selector, while the Call tab shows the underlying Playwright API call with its arguments.
- Before snapshot: the page just prior to the action firing.
- After snapshot: the result, including any navigation or DOM mutation.
- Pop out: open the snapshot in a real browser tab to inspect it with native DevTools.
Because the snapshot is a live DOM clone rather than a flat image, you can open your browser DevTools against the popped-out snapshot and inspect computed styles, the accessibility tree, and element attributes exactly as they were during the run.
Watch Mode: Edit, Save, Re-Run Instantly
The productivity multiplier in UI Mode is watch mode. Click the eye icon next to a test (or the tree root) and Playwright re-runs that test every time you save the spec file. This turns test authoring into a tight feedback loop: tweak a locator, hit save, and watch the run replay in under a second without ever touching the terminal.
import { test, expect } from '@playwright/test';
// Toggle the eye icon on this test in UI Mode, then keep editing.
// Every Cmd/Ctrl+S triggers an automatic re-run.
test('cart updates the badge count', async ({ page }) => {
await page.goto('https://demo.playwright.dev/todomvc');
const newTodo = page.getByPlaceholder('What needs to be done?');
await newTodo.fill('Write the UI Mode guide');
await newTodo.press('Enter');
// If this count is wrong, fix it, save, and watch mode re-runs immediately
await expect(page.getByTestId('todo-count')).toContainText('1');
});
Watch mode also respects the test filter bar at the top of the tree. You can filter by status (failed, passed, skipped), by tag such as @smoke, by project, or by free-text search, and only the matching tests will watch-and-rerun. This keeps the loop fast even in a suite with thousands of tests.
The Pick Locator Tool
Click the Pick locator button, then hover over any element in the snapshot. UI Mode generates the recommended user-facing locator and copies it to your clipboard. It prioritizes role and accessible-name based selectors, which are far more resilient than brittle CSS or XPath. The result usually looks like one of these:
// What the Pick locator tool typically produces, best to worst preference:
page.getByRole('button', { name: 'Add to cart' });
page.getByLabel('Email address');
page.getByPlaceholder('Search products');
page.getByText('Order confirmed');
page.getByTestId('checkout-submit'); // when you add data-testid attributes
// You can edit the picked locator live in the locator box and UI Mode
// highlights every matching element in the snapshot as you type.
page.getByRole('listitem').filter({ hasText: 'In stock' });
The editable locator box is the hidden gem here. Paste or type any locator and UI Mode highlights all matches in real time, so you can confirm a selector is unique before you commit it to a test. If you rely on test IDs, configure the attribute name once in your config so getByTestId targets it:
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
// Make getByTestId() match data-qa instead of the default data-testid
testIdAttribute: 'data-qa',
// Capture a trace on the first retry so failures are always inspectable
trace: 'on-first-retry',
},
});
Network, Console, and the Call Log
When a test fails because of a backend response rather than the UI, the Network tab is your friend. It lists every request the page made during the selected action window, with method, status, content type, size, and timing. Click a request to see its full headers and response body. Pair this with page.route to stub an endpoint and reproduce edge cases deterministically.
import { test, expect } from '@playwright/test';
test('shows an error banner when the API returns 500', async ({ page }) => {
// Intercept the products call and force a server error
await page.route('**/api/products', async (route) => {
await route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ message: 'Internal Server Error' }),
});
});
await page.goto('https://example.com/store');
// In UI Mode the Network tab shows the mocked 500 response inline
await expect(page.getByRole('alert')).toContainText('Something went wrong');
});
The Console tab surfaces anything the page logged, including uncaught JavaScript errors that often explain a mysterious UI failure. The Log (Call) tab shows Playwright’s own narration of each action, such as how long it waited for an element to become actionable and which locator it resolved. Together these three tabs usually pinpoint a failure in seconds.
🚀 Level Up Your Playwright
From locators to CI pipelines — build a production-grade Playwright + TypeScript framework step by step.
UI Mode vs Other Debugging Tools
UI Mode is not the only way to debug Playwright, but it is the best default for local iteration. Here is how it compares to the alternatives you already have:
| Tool | Command | Best for | Time-travel? |
|---|---|---|---|
| UI Mode | --ui | Local authoring, watch loop, picking locators | Yes |
| Trace Viewer | show-trace | Post-mortem on a CI failure from a trace.zip | Yes |
| Headed mode | --headed | Watching the real browser drive live | No |
| Inspector / Debug | --debug | Stepping with breakpoints and the live locator picker | No |
A neat workflow detail: UI Mode and Trace Viewer share the same engine. The trace files produced in CI (configured via trace: 'on-first-retry') open in the very same interface with npx playwright show-trace trace.zip. So the skills you build debugging locally transfer directly to triaging CI failures.
Pro Tips to Get the Most From UI Mode
- Filter aggressively. Use the status and tag filters in the tree to isolate only failing or only
@smoketests before enabling watch mode. - Pop out snapshots. Open the After snapshot in a tab to run real DevTools, inspect the accessibility tree, and verify computed styles.
- Combine with soft assertions. Using
expect.softlets a test continue past a failed check, so UI Mode shows you all the assertion failures in one run instead of stopping at the first. - Mock time and network. Pair UI Mode with
page.clockfor date-dependent UIs andpage.routeFromHARto replay recorded traffic deterministically. - Keep traces on retry. Set
trace: 'on-first-retry'so any flaky CI run is replayable in the same time-travel interface.
import { test, expect } from '@playwright/test';
test('billing summary renders all rows', async ({ page }) => {
await page.goto('https://example.com/billing');
// Soft assertions: UI Mode reports every failure, not just the first one
await expect.soft(page.getByTestId('subtotal')).toHaveText('$120.00');
await expect.soft(page.getByTestId('tax')).toHaveText('$9.60');
await expect.soft(page.getByTestId('total')).toHaveText('$129.60');
// The test still fails if any soft assertion failed, but you see them all
});
Mastering this Playwright UI mode guide turns debugging from a guessing game into a precise, visual investigation. Instead of sprinkling logs and re-running blindly, you select the failing action, read its Before snapshot, confirm the locator, check the network response, and fix the root cause in one pass. Add watch mode and the pick-locator tool on top, and UI Mode becomes the fastest place to both write and repair Playwright tests in TypeScript.
FAQ
Do I need to install anything extra to use Playwright UI Mode?
No. UI Mode ships inside the @playwright/test package, so any project that already runs Playwright tests can launch it with npx playwright test --ui. There is no separate dependency, browser extension, or GUI library to install, and it works the same on macOS, Windows, and Linux.
What is the difference between UI Mode and the Trace Viewer?
They share the same interface and time-travel engine, but UI Mode is for live, interactive local development with watch mode and re-running, while the Trace Viewer is for post-mortem analysis of a recorded trace.zip file, typically downloaded from a CI failure. Use UI Mode while writing tests and the Trace Viewer (npx playwright show-trace) to triage failures after the fact.
Can UI Mode help me fix flaky tests?
Yes. By stepping through the Actions list and comparing the Before and After snapshots, you can see whether an element was covered, not yet rendered, or in an unexpected state when the action fired. The Network and Console tabs reveal slow or failed requests and JavaScript errors that cause intermittent failures. Combined with trace: 'on-first-retry', you can capture the exact flaky run and replay it deterministically.
🎓 Master Playwright End to End
Join hundreds of SDETs building real automation frameworks. Lifetime access, hands-on projects, and a job-ready portfolio.
