Playwright Locators: Complete 2026 Guide
Complete 2026 Playwright locators guide. Role-based, test ID, label, text, CSS, XPath, and relative locators with priority order and best practices.

In this article
This guide covers every Playwright locator strategy in 2026 with priority order, examples, and best practices for stable, maintainable tests. For the broader context, read the Playwright Complete Guide and the Playwright POM in TypeScript walkthrough.
Why Locators Matter
Locators are how your tests find elements on the page. The right strategy is the difference between:
- Stable tests — pass consistently across releases.
- Flaky tests — break when developers rename or restructure markup.
Locator Priority Order
| Priority | Locator | Best for |
|---|---|---|
| 1 | Role (getByRole) | All elements with semantic roles |
| 2 | Test ID (getByTestId) | Elements with explicit test IDs |
| 3 | Label (getByLabel) | Form fields with labels |
| 4 | Placeholder (getByPlaceholder) | Form fields with placeholders |
| 5 | Text (getByText) | Buttons, links, static text |
| 6 | Alt text (getByAltText) | Images with alt text |
| 7 | Title (getByTitle) | Elements with title attributes |
| 8 | CSS (locator('.btn')) | Last resort |
| 9 | XPath (locator('xpath=...')) | Truly last resort |
Role-Based Locators
The recommended default in 2026. Playwright uses the accessibility tree, which is more stable than DOM structure.
// Button
await page.getByRole('button', { name: 'Submit' }).click();
// Link
await page.getByRole('link', { name: 'Sign up' }).click();
// Text input
await page.getByRole('textbox', { name: 'Email' }).fill('admin@example.com');
// Checkbox
await page.getByRole('checkbox', { name: 'Remember me' }).check();
// Dropdown
await page.getByRole('combobox', { name: 'Country' }).selectOption('US');
// Heading
await expect(page.getByRole('heading', { name: 'Welcome' })).toBeVisible();
// Dialog
await page.getByRole('dialog').waitFor();Why role-based is best
- Tests fail loudly when accessibility breaks.
- Tests are resilient to DOM changes (CSS classes, structure).
- Tests double as accessibility checks.
Test ID Locators
The recommended second choice when role-based doesn't work.
<!-- HTML -->
<button data-testid="submit-order">Submit Order</button>await page.getByTestId('submit-order').click();Best practices
- Use
data-testid(the Playwright convention). - Don't add test IDs to every element — only ones tests interact with.
- Use kebab-case naming.
Label & Placeholder Locators
Label
<label for="email">Email address</label>
<input id="email" type="email" />await page.getByLabel('Email address').fill('admin@example.com');
// Partial match
await page.getByLabel(/email/i).fill('admin@example.com');Placeholder
await page.getByPlaceholder('you@example.com').fill('admin@example.com');Text, Alt & Title Locators
Text
// Exact
await page.getByText('Submit Order').click();
// Partial
await page.getByText('Submit', { exact: false }).click();
// Regex
await page.getByText(/submit/i).click();Alt text
await page.getByAltText('Company Logo').click();Title
await page.getByTitle('Close dialog').click();CSS & XPath (Last Resort)
CSS
await page.locator('.btn-primary').click();
await page.locator('#submit').click();
await page.locator('[type="submit"]').click();
await page.locator('form.login button[type="submit"]').click();XPath
await page.locator('xpath=//button[contains(text(), "Submit")]').click();Use XPath only when no semantic role/label exists, the element is in a third-party component, or you need to traverse a complex DOM hierarchy.
Chaining and Filtering
Filter by text
await page.locator('tr').filter({ hasText: 'Active' }).click();Filter by child
await page.locator('article').filter({ has: page.getByRole('button') }).click();Chain to nth
// 3rd cell in 5th row
await page.locator('tr').nth(4).locator('td').nth(2).click();Locator Best Practices
Do
- Use role-based locators first.
- Add
data-testidto critical custom widgets. - Chain locators for complex queries.
- Filter to disambiguate.
- Use regex for flexible matches.
- Store locators as private fields in page objects.
Don't
- Don't use absolute XPath.
- Don't rely on CSS-in-JS classes that change every build.
- Don't hardcode text that breaks with i18n.
- Don't chain five selectors when a role works.
- Don't use index-based locators without a clear reason.
Common Patterns
Login form
await page.getByLabel('Email').fill('admin@example.com');
await page.getByLabel('Password').fill('Sup3rSecret!');
await page.getByRole('button', { name: 'Sign in' }).click();Search box
await page.getByRole('searchbox', { name: 'Search' }).fill('playwright');
await page.getByRole('button', { name: 'Search' }).click();Modal dialog
await page.getByRole('dialog').waitFor();
await page.getByRole('button', { name: 'Confirm' }).click();Table row by ID
const row = page.locator('tr').filter({ hasText: '123' });
await row.getByRole('button', { name: 'Edit' }).click();iFrame content
const frame = page.frameLocator('#payment-iframe');
await frame.getByRole('button', { name: 'Pay' }).click();Common Mistakes and Fixes
1. Absolute XPath
// BAD
await page.locator('xpath=/html/body/div[3]/form/input[1]').click();
// GOOD
await page.getByLabel('Email').fill('admin@example.com');2. CSS-in-JS classes
// BAD
await page.locator('.MuiButton-root-123').click();
// GOOD
await page.getByRole('button', { name: 'Submit' }).click();3. Hardcoded text
// BAD
await page.getByText('Submit Order').click();
// GOOD
await page.getByRole('button', { name: /submit.*order/i }).click();4. Over-combined selectors
// BAD
await page.locator('div.container > form > div.row > button[type="submit"]').click();
// GOOD
await page.getByRole('button', { name: 'Submit' }).click();5. Index-based selection
// BAD
await page.locator('tr').nth(4).locator('td').nth(2).click();
// GOOD
await page.locator('tr').filter({ hasText: '123' }).getByRole('button', { name: 'Edit' }).click();Keep Learning
Continue your Playwright journey: Playwright Complete Guide · Playwright POM in TypeScript · Playwright TypeScript Tutorial · Playwright interview questions · AI mock interview. Official reference: Playwright locators docs.
Frequently asked questions
What is the best locator strategy in Playwright?
Role-based (getByRole) is the recommended default in 2026. It's stable, accessible, and resilient to DOM changes.
Should I use data-testid or role-based locators?
Prefer role-based. Use data-testid for elements without semantic roles (e.g., custom widgets).
How do I handle dynamic content with locators?
Use filter() to disambiguate, regex for flexible matches, and wait for specific conditions via waitFor or expect.
What if there's no good role or test ID?
Use text, label, or placeholder. Avoid XPath unless absolutely necessary.
How do I debug locator issues?
Use Playwright's Trace Viewer, or page.locator('selector').count() to verify your selector matches.
What's the difference between locator() and getBy*?
page.locator() returns a Locator from a selector string. page.getBy*() returns a Locator using a semantic API. Both are lazy and re-evaluated.
Practice these questions
Drill 200+ Playwright questions with senior-SDET sample answers — locators, auto-wait, fixtures, parallelism and trace viewer.
Was this article helpful?
Keep building your QA edge
Pillar guides- Selenium Pillarthe full Selenium reference300 Selenium WebDriver Q&A — locators, waits, frameworks.
- AI Mock InterviewSoftwareTestPilot's AI interview coachLive AI-powered mock interviews with rubric feedback.
- ATS Resume ReviewSoftwareTestPilot's ATS resume checkerFree AI ATS scoring with rewrite suggestions.
Continue reading

Why Every QA Engineer Must Master CI/CD Pipelines in 2026 (Or Risk Obsolescence)
12 min read
Is Cypress Dead? Analyzing 2026 Playwright Market Share
12 min read
Why Tests Pass Locally But Fail in CI/CD (And the 6 Fixes That Actually Work in 2026)
13 min readJoin the QA Community
Connect with fellow testers, share job leads, and get career advice.
Stop Reinventing the Wheel. Upgrade Your QA Arsenal.
Take your testing skills from beginner to Lead Engineer. Supercharge your daily workflow with our premium digital resources.
- ⚡ Ready-to-use testing strategy templates
- 🔥 Advanced API & UI automation guides
- ⏱️ Save 10+ hours a week on test planning