Playwright Locators with TypeScript – Complete Guide for Beginners to Advanced

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

RoleExample
buttonLogin Button
textboxInput field
checkboxRemember Me
radioGender
headingH1-H6
linkHome
comboboxDropdown
menuitemMenu
dialogPopup
imgImages

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

  1. Prefer getByRole() for interactive elements.
  2. Use getByLabel() for form fields.
  3. Use getByTestId() for stable automation.
  4. Avoid brittle XPath expressions whenever possible.
  5. Use locator chaining to narrow searches.
  6. Keep selectors short, readable, and maintainable.
  7. Use assertions such as toBeVisible() before complex interactions when they improve readability.
  8. Avoid using fixed waits like waitForTimeout(). Rely on Playwright’s auto-waiting.
  9. Store commonly used locators inside Page Object Model (POM) classes.
  10. Avoid using .nth() unless the order of elements is guaranteed.

Locator Priority (Recommended Order)

PriorityLocatorRecommended
1getByRole()⭐⭐⭐⭐⭐
2getByLabel()⭐⭐⭐⭐⭐
3getByTestId()⭐⭐⭐⭐⭐
4getByPlaceholder()⭐⭐⭐⭐
5getByText()⭐⭐⭐⭐
6CSS Selectors⭐⭐⭐
7XPath⭐⭐

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() and getByLabel().
  • 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.

Leave a Comment