Modern QA2026The Locator Hierarchy — tiles
Log inJoin
71 / 139 · 13 Browser Automation with Playwright · Locator Strategies← prev⊞ allnext →☰ Read as one page

7.2The Locator Hierarchy

Playwright recommends locators in this priority order:

Priority Locator Example Why
1 Role getByRole('button', { name: 'Submit' }) Mirrors accessibility tree; resilient to markup changes
2 Text getByText('Sign in') User-visible; breaks only when copy changes
3 Test ID getByTestId('login-form') Explicit contract between dev and test
4 Label getByLabel('Email address') Form fields via associated label
5 Placeholder getByPlaceholder('Enter email') Inputs without visible labels
6 CSS/XPath page.locator('.btn-primary') Last resort for complex DOM traversal

Role-Based Locators (Preferred)

Role locators use the accessibility tree, not the DOM structure. They find elements the way a screen reader would — which means they are resilient to HTML refactoring.

// Finds <button>, <input type="submit">, or any element with role="button"
await page.getByRole('button', { name: 'Submit' }).click();

// Finds <a> elements or elements with role="link"
await page.getByRole('link', { name: 'Sign up' }).click();

// Finds <h1>–<h6> or elements with role="heading"
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();

// Checkboxes and radio buttons
await page.getByRole('checkbox', { name: 'Remember me' }).check();

Text Locators

// Exact text
await page.getByText('Welcome back', { exact: true }).click();

// Substring match (default)
await page.getByText('Welcome').click();

// Regex
await page.getByText(/welcome/i).click();

Test ID Locators

When role and text locators are not viable (e.g., elements without meaningful text or multiple identical elements), test IDs provide a stable contract.

// In the application: <div data-testid="user-profile">...</div>
await page.getByTestId('user-profile').click();

// Configure the attribute name in playwright.config.ts:
// use: { testIdAttribute: 'data-qa' }

Label and Placeholder Locators

// Finds input by its associated <label>
await page.getByLabel('Email address').fill('user@example.com');

// Finds input by placeholder text
await page.getByPlaceholder('Search products').fill('laptop');