Introduction
Locators are one of the most important concepts in Playwright. A locator is used to find and interact with elements on a web page. Unlike traditional Selenium locators, Playwright locators are smart, reliable, auto-waiting, and resilient.
Playwright automatically waits for elements to become:
- Visible
- Stable
- Enabled
- Ready for interaction
This significantly reduces flaky test cases.
What is a Locator?
A locator is an object that identifies one or more elements on a web page.
Example:
const username = page.locator('#username');
The locator does not immediately search for the element.
Instead, Playwright waits until the action is performed.
Example:
await page.locator('#username').fill('Admin');
Why Use Playwright Locators?
Advantages include:
- Auto waiting
- Better stability
- Retry mechanism
- Cleaner syntax
- Supports chaining
- Easy filtering
- Works well with dynamic pages
- Recommended by Playwright team
Basic Syntax
const locator = page.locator('selector');
Example
await page.locator('#email').fill('test@test.com');
Types of Playwright Locators
Playwright provides multiple locator strategies.
1. getByRole()
Recommended by Playwright.
Best for accessible applications.
Example
await page.getByRole('button', {
name: 'Login'
}).click();
HTML
<button>Login</button>
Common Roles
| Role | Example |
|---|---|
| button | Login Button |
| textbox | Input field |
| checkbox | Remember Me |
| radio | Gender |
| heading | H1-H6 |
| link | Home |
| combobox | Dropdown |
| menuitem | Menu |
| dialog | Popup |
| img | Images |
Example
await page.getByRole('textbox').fill('Admin');
2. getByText()
Find element using visible text.
Example
await page.getByText('Submit').click();
Partial text
await page.getByText('Submit', {
exact: false
}).click();
Exact text
await page.getByText('Submit', {
exact: true
}).click();
3. getByLabel()
Best for form automation.
HTML
<label>Email</label>
<input>
Playwright
await page.getByLabel('Email').fill('abc@test.com');
4. getByPlaceholder()
Locate input by placeholder.
HTML
<input placeholder="Enter username">
Playwright
await page.getByPlaceholder('Enter username').fill('Admin');
5. getByAltText()
Locate images.
HTML
<img alt="Company Logo">
Playwright
await page.getByAltText('Company Logo').click();
6. getByTitle()
Locate using title attribute.
HTML
<button title="Delete Record">
Playwright
await page.getByTitle('Delete Record').click();
7. getByTestId()
Highly recommended for automation.
HTML
<button data-testid="loginBtn">
Playwright
await page.getByTestId('loginBtn').click();
Configure custom attribute
use: {
testIdAttribute: 'data-test'
}
8. CSS Locator
await page.locator('#username');
Class
await page.locator('.btn-primary');
Attribute
await page.locator('[type="submit"]');
Multiple classes
await page.locator('.btn.primary.large');
9. XPath Locator
await page.locator('//button[text()="Login"]');
Relative XPath
await page.locator('//input[@id="email"]');
Although supported, CSS or Playwright-specific locators are generally preferred because they are easier to maintain.
10. ID Locator
await page.locator('#login');
11. Class Locator
await page.locator('.login-button');
12. Name Attribute
await page.locator('[name="username"]');
13. Attribute Locator
await page.locator('[placeholder="Search"]');
14. Text Selector
await page.locator('text=Login');
Locator Chaining
Locate child elements.
HTML
<div class="card">
<button>Edit</button>
</div>
Playwright
await page
.locator('.card')
.locator('button')
.click();
Filter Locator
await page.locator('.card').filter({
hasText: 'Laptop'
}).click();
has()
Locate parent having child.
await page.locator('div', {
has: page.locator('button')
});
hasText()
await page.locator('li', {
hasText: 'India'
});
nth()
Locate by index.
await page.locator('.product').nth(0);
Second element
await page.locator('.product').nth(1);
first()
await page.locator('.item').first();
last()
await page.locator('.item').last();
Locator Count
const total = await page.locator('.row').count();
console.log(total);
Iterate Multiple Elements
const products = page.locator('.product');
const count = await products.count();
for (let i = 0; i < count; i++) {
console.log(
await products.nth(i).textContent()
);
}
allTextContents()
const names =
await page.locator('.name').allTextContents();
console.log(names);
allInnerTexts()
const values =
await page.locator('.city').allInnerTexts();
Locator Actions
Click
await page.locator('#login').click();
Double Click
await page.locator('#save').dblclick();
Right Click
await page.locator('#menu').click({
button: 'right'
});
Fill
await page.locator('#email').fill('abc@test.com');
Type Slowly
await page.locator('#email').pressSequentially(
'Admin'
);
Clear Text
await page.locator('#email').clear();
Press Keyboard
await page.locator('#search').press('Enter');
Check Checkbox
await page.locator('#remember').check();
Uncheck
await page.locator('#remember').uncheck();
Hover
await page.locator('#menu').hover();
Focus
await page.locator('#email').focus();
Drag and Drop
await page.locator('#source')
.dragTo(
page.locator('#target')
);
Locator Assertions
Visible
await expect(
page.locator('#login')
).toBeVisible();
Hidden
await expect(
page.locator('#popup')
).toBeHidden();
Enabled
await expect(
page.locator('#save')
).toBeEnabled();
Disabled
await expect(
page.locator('#save')
).toBeDisabled();
Checked
await expect(
page.locator('#remember')
).toBeChecked();
Contains Text
await expect(
page.locator('h1')
).toContainText('Dashboard');
Exact Text
await expect(
page.locator('h1')
).toHaveText('Dashboard');
Count
await expect(
page.locator('.row')
).toHaveCount(5);
Working with Multiple Locators
const rows = page.locator('table tbody tr');
for(let i = 0; i < await rows.count(); i++){
console.log(
await rows.nth(i).textContent()
);
}
Strict Mode
Playwright locators are strict by default. If a locator matches more than one element, Playwright throws an error.
Example:
await page.getByRole('button').click();
Error:
Error:
Locator resolved to multiple elements
Solution:
await page.getByRole('button').nth(0).click();
or
await page.getByRole('button', {
name: 'Login'
}).click();
Best Practices
- Prefer
getByRole()for interactive elements. - Use
getByLabel()for form fields. - Use
getByTestId()for stable automation. - Avoid brittle XPath expressions whenever possible.
- Use locator chaining to narrow searches.
- Keep selectors short, readable, and maintainable.
- Use assertions such as
toBeVisible()before complex interactions when they improve readability. - Avoid using fixed waits like
waitForTimeout(). Rely on Playwright’s auto-waiting. - Store commonly used locators inside Page Object Model (POM) classes.
- Avoid using
.nth()unless the order of elements is guaranteed.
Locator Priority (Recommended Order)
| Priority | Locator | Recommended |
|---|---|---|
| 1 | getByRole() | ⭐⭐⭐⭐⭐ |
| 2 | getByLabel() | ⭐⭐⭐⭐⭐ |
| 3 | getByTestId() | ⭐⭐⭐⭐⭐ |
| 4 | getByPlaceholder() | ⭐⭐⭐⭐ |
| 5 | getByText() | ⭐⭐⭐⭐ |
| 6 | CSS Selectors | ⭐⭐⭐ |
| 7 | XPath | ⭐⭐ |
Common Mistakes
- Using long and fragile XPath expressions.
- Depending on dynamically generated CSS classes.
- Selecting elements by index when a unique locator is available.
- Using
waitForTimeout()instead of Playwright’s built-in waiting. - Ignoring accessibility-based locators such as
getByRole()andgetByLabel(). - Repeating the same locator across multiple test files instead of centralizing them in Page Objects.
Real-World Login Example
import { test, expect } from '@playwright/test';
test('User Login', async ({ page }) => {
await page.goto('https://example.com/login');
await page.getByLabel('Username').fill('admin');
await page.getByLabel('Password').fill('admin123');
await page.getByRole('button', { name: 'Login' }).click();
await expect(
page.getByRole('heading', { name: 'Dashboard' })
).toBeVisible();
});
Summary
Playwright locators are designed to create reliable, readable, and maintainable UI automation tests. Instead of relying on fragile selectors, Playwright encourages the use of accessibility-aware locators such as getByRole(), getByLabel(), and getByTestId(). By combining locator chaining, filtering, built-in assertions, and auto-waiting, you can build test suites that are less flaky and easier to maintain over time.
Mastering Playwright locators is a foundational skill for any automation engineer and is essential for developing scalable Playwright frameworks using TypeScript.