SoftwareTestPilot
Automation TestingPublished: 9 min read

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.

Avinash Kamble
Avinash Kamble
Founder & QA Engineer at SoftwareTestPilot
Reviewed by Priyanka G.
Share:XLinkedInWhatsApp
Playwright Locators guide cover — accessibility tree nodes targeted by a selector reticle on a dark navy background.
Playwright Locators guide cover — accessibility tree nodes targeted by a selector reticle on a dark navy background.
In this article
  1. Why Locators Matter
  2. Locator Priority Order
  3. Role-Based Locators
  4. Test ID Locators
  5. Label & Placeholder Locators
  6. Text, Alt & Title Locators
  7. CSS & XPath (Last Resort)
  8. Chaining and Filtering
  9. Locator Best Practices
  10. Common Patterns
  11. Common Mistakes and Fixes
  12. Keep Learning
  13. Frequently asked questions

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

PriorityLocatorBest for
1Role (getByRole)All elements with semantic roles
2Test ID (getByTestId)Elements with explicit test IDs
3Label (getByLabel)Form fields with labels
4Placeholder (getByPlaceholder)Form fields with placeholders
5Text (getByText)Buttons, links, static text
6Alt text (getByAltText)Images with alt text
7Title (getByTitle)Elements with title attributes
8CSS (locator('.btn'))Last resort
9XPath (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-testid to 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();

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.

Keep going

Practice these questions

Drill 200+ Playwright questions with senior-SDET sample answers — locators, auto-wait, fixtures, parallelism and trace viewer.

Found this useful?
Share:XLinkedInWhatsApp

Was this article helpful?

Keep building your QA edge

Continue reading

Join the QA Community

Connect with fellow testers, share job leads, and get career advice.

Premium QA Resources

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
4.9/5 rating
Explore All Products

⭐⭐⭐⭐⭐ Trusted by 1,000+ Software Test Pilots • Instant Access