Playwright Locators

A Locator is one of the core concepts in Playwright. It represents a way to find elements on a page.

This chapter introduces Playwright's recommended locating methods: locating elements by role, text, label, and placeholder.


What is a Locator

A Locator is an abstract object that Playwright uses to find elements on a page.

Unlike traditionaldocument.querySelector()ways, Playwright's Locator hasautomatic waiting and automatic retryingcapabilities.

When the page content changes dynamically, the Locator will continuously re-find elements until it finds a matching element or times out.

Example

// Create a Locator (no element lookup happens immediately)
const button = page.getByRole('button', { name: 'Submit' });

// When performing an action, Playwright automatically waits for the button to appear and become actionable
await button.click();

How Locators Work

When you create a Locator, Playwright only records a lookup rule.

When you call.click()、.fill()actions orexpect(locator)assertions, the Locator actually starts to find elements.

If the element cannot be found, the Locator will keep retrying until the operation times out.


Locating by Role: getByRole()

getByRole()is Playwright's most recommended locating method.

It uses the element'saccessibility role (ARIA Role)to find elements, which is the closest to how users perceive them.

Basic Usage

Example

// Locate a button - by role "button"
const submitBtn = page.getByRole('button', { name: 'Submit' });
await submitBtn.click();

// Locate a link - by role "link"
const homeLink = page.getByRole('link', { name: 'Home' });
await homeLink.click();

// Locate a heading - by role "heading"
const mainTitle = page.getByRole('heading', { name: 'Welcome to EXAMPLE' });

// Locate a text box - by role "textbox"
const searchBox = page.getByRole('textbox', { name: 'Search' });
await searchBox.fill('Playwright');

Common ARIA Roles

RoleCorresponding HTML ElementsUsage Example
'button'<button>、role="button"getByRole('button', { name: '登录' })
'link'<a>getByRole('link', { name: '了解更多' })
'heading'<h1>~<h6>getByRole('heading', { name: 'EXAMPLE 教程' })
'textbox'<input type="text">、<textarea>getByRole('textbox', { name: '用户名' })
'checkbox'<input type="checkbox">getByRole('checkbox', { name: '记住我' })
'radio'<input type="radio">getByRole('radio', { name: '男' })
'combobox'<select>getByRole('combobox', { name: '城市' })
'img'<img>getByRole('img', { name: 'Logo' })
'list'<ul>、<ol>getByRole('list')
'listitem'<li>getByRole('listitem')
'navigation'<nav>getByRole('navigation')
'table'<table>getByRole('table')

The name Option

nameThe option uses the element'saccessible nameto narrow down the scope.

Sources of the accessible name include: the element's text content,aria-labelattributes, associated<label>etc.

Example

// Match by button text
page.getByRole('button', { name: 'Login' });

// Match by aria-label
page.getByRole('button', { name: 'Close dialog' });

// Regular expression matching
page.getByRole('link', { name: /EXAMPLE|rookie/ });

State Options

In addition toname,getByRoleit also supports multiple state options:

Example

// Locate the checked checkbox
page.getByRole('checkbox', { checked: true });

// Locate the disabled button
page.getByRole('button', { disabled: true });

// Locate the expanded dropdown menu
page.getByRole('button', { expanded: true });

// Locate the selected option
page.getByRole('option', { selected: true });

// Combine multiple states
page.getByRole('checkbox', { name: 'Agree to the terms', checked: false });

Locating by Text: getByText()

getByText()By the element'stext contentto locate.

Example

// Exact text match
page.getByText('Welcome to EXAMPLE');

// Partial match (default)
page.getByText('EXAMPLE');

// Force exact match
page.getByText('EXAMPLE', { exact: true });

// Regular expression match
page.getByText(/EXAMPLE|rookie tutorial/);

getByTextIt matches the complete text content of the element, sogetByText('EXAMPLE', { exact: true })it will only match elements whose text content is exactly "EXAMPLE".

By default, when exact matching is not used, substring matching is sufficient (for example,getByText('EXAMPLE')it will match "Welcome to EXAMPLE").


Locating by Label: getByLabel()

getByLabel()Used to locate<label>associated form controls.

Example

// HTML structure:
// <label for="username">Username</label>
// <input id="username" type="text" />

// Locate the input via the label text
await page.getByLabel('Username').fill('example_user');

// HTML structure:
// <label>Password <input type="password" /></label>

// Nested labels are also valid
await page.getByLabel('Password').fill('password123');

It also supports{ exact: true }exact matching options.


Locating by Placeholder: getByPlaceholder()

getByPlaceholder()Through theplaceholderplaceholder attribute to locate input fields.

Example

// HTML structure: <input placeholder="Please enter a search keyword" />
await page.getByPlaceholder('Please enter a search keyword').fill('Playwright');

// HTML structure: <textarea placeholder="Tell us what you think..."></textarea>
await page.getByPlaceholder('Tell us what you think...').fill('The EXAMPLE tutorial is great!');

Choosing Between getByLabel and getByPlaceholder

MethodApplicable scenarioPriority
getByLabel()When there is a <label> elementUse it first
getByPlaceholder()When there is no label, but there is a placeholderSecond choice

getByRoleis Playwright's most recommended locating method because it simulates how users identify elements by role. The next isgetByLabelandgetByPlaceholder. Only consider using it when there are no semantic elementsgetByTestIdor CSS selectors.


getByAltText() for Image Locating

getByAltText()Through the image'saltalt attribute to locate<img>the <img> element.

Example

// HTML:<img src="logo.png" alt="EXAMPLE Logo" />
await expect(page.getByAltText('EXAMPLE Logo')).toBeVisible();

// Regular expression match
await expect(page.getByAltText(/logo/i)).toBeVisible();

getByTitle() for Title Attribute Locating

getByTitle()Through the element'stitletitle attribute to locate.

Example

// HTML: <button title="Close current page">X</button>
await page.getByTitle('Close current page').click();

getByAltTextandgetByTitleUsed less frequently; only when the element has an explicit alt or title attribute and cannot be located by other means.


getByTestId() for Test ID Locating

getByTestId()YesFallback solution, viadata-testidattribute to locate the element.

Example

// HTML: <button data-testid="submit-btn">Submit</button>
await page.getByTestId('submit-btn').click();

// Can contain multiple matches (e.g., list items)
const items = page.getByTestId('todo-item');
await expect(items).toHaveCount(3);

Customizing the testId Attribute

If your project does not usedata-testid, you canplaywright.config.tscustomize the attribute name in:

Example

// File path: playwright.config.ts
export default defineConfig({
  use: {
    // Use data-cy instead of data-testid (for Cypress migration)
    testIdAttribute: 'data-cy',
    // Or use id as testId
    // testIdAttribute: 'id',
  },
});

page.locator() CSS and XPath Locating

page.locator()Supports CSS selectors and XPath, serving as a more flexible fallback.

CSS Selectors

Example

// By CSS class name
await page.locator('.submit-btn').click();

// By ID
await page.locator('#username').fill('example_user');

// By attribute
await page.locator('[data-type="primary"]').click();

// By tag + class combination
await page.locator('button.primary').click();

// By parent-child relationship
await page.locator('nav a').first().click();

// CSS selector containing specific text
await page.locator('button:has-text("Submit")').click();

XPath Locating

Example

// Locate using XPath
await page.locator('xpath=//button[@type="submit"]').click();

// Locate by text
await page.locator('xpath=//h1[contains(text(), "EXAMPLE")]').click();

CSS selectors and XPath are the last resort; prioritize usinggetByRole、getByTextsemantic location methods. CSS selectors are prone to breaking due to style refactoring, and XPath has poor readability and slower performance.


Locator Chaining

Playwright's Locator supports chaining, allowing further filtering on the current matching results.

.first()、.last()、.nth()

Example

// Get the first matching list item
const firstItem = page.getByRole('listitem').first();

// Get the last one
const lastItem = page.getByRole('listitem').last();

// Get the 2nd one (index starts at 0)
const secondItem = page.getByRole('listitem').nth(1);

.filter() Filtering

Example

// Filter buttons containing specific text
page.getByRole('button').filter({ hasText: 'Save' });

// Filter out those not containing specific text
page.getByRole('listitem').filter({ hasNotText: 'Deleted' });

// Filter those containing a specific child element
page.getByRole('listitem').filter({ has: page.getByRole('button') });

// Filter those not containing a specific child element
page.getByRole('listitem').filter({ hasNot: page.getByTestId('badge') });

.locator() for Child Element Locating

Example

// First locate the list container, then find buttons within it
const list = page.getByTestId('todo-list');
const deleteBtn = list.locator('button.delete');
await deleteBtn.click();

// Multi-level nesting
page
  .getByRole('dialog')
  .locator('div.content')
  .locator('button')
  .filter({ hasText: 'Confirm' })
  .click();

.and() and .or() Logical Combinations (1.63+)

Example

// Satisfy both conditions simultaneously
const btn = page.getByRole('button')
  .and(page.getByText('Submit'));

// Satisfy either condition
const target = page.getByText('EXAMPLE')
  .or(page.getByTitle('EXAMPLE'));

Summary of Locator Strategy Priority

Arranged from most to least recommended:

PriorityMethodUsage condition
1 (Most recommended)getByRole()Element has a clear ARIA role
2getByLabel()Form element has an associated label
3getByPlaceholder()Input field has a placeholder
4getByText()Element has clear text content
5getByAltText()Image has alt attribute
6getByTitle()Element has title attribute
7 (Fallback)getByTestId()When unable to locate using the above methods
8 (Last resort)locator()CSS selectors / XPath
Other extensions