Playwright

Playwright Locators: How to Find Elements Reliably

Learn how Playwright locators work, how to choose role, label, text, and test-id locators, and how strictness and web-first assertions affect reliability.

Software Testing Automation Editorial TeamEditorial publishing identity
Published
Reading time
3 min read
Difficulty
Beginner
Audience
For Automation tester, Developer, QA engineer
A crosshair locked onto a single point within a grid of dots
IllustrationA crosshair locked onto a single point within a grid of dots.

Article overview

What you will learn

  • What a locator represents
  • How role, label, placeholder, text, alt, title, and test-id locators differ
  • How strictness, chaining, and filtering work
  • Why first, last, and nth can hide ambiguity
  • How web-first assertions improve observable checks

Before you begin

Prerequisites

  • Node.js installed
  • An existing Playwright Test project
  • Basic TypeScript or JavaScript knowledge

What is a locator?

Locator stability matters because UI structure changes often. A selector tied to several container classes can break during harmless redesign work. A locator based on a button’s role and accessible name often expresses user intent more directly.

LocatorTypical useExample
getByRoleControls and landmarks with an accessible namegetByRole('button', { name: 'Sign in' })
getByLabelForm controls associated with a labelgetByLabel('Email')
getByPlaceholderA placeholder when no stronger label existsgetByPlaceholder('name@example.com')
getByTextVisible non-interactive textgetByText('Order confirmed')
getByAltTextImages with alternative textgetByAltText('Company logo')
getByTitleElements with a title attributegetByTitle('Issues count')
getByTestIdAn explicit test contractgetByTestId('checkout-total')

Practical login-form example

tests/login.spec.ts TypeScript
import { test, expect } from '@playwright/test'; test('shows an error for invalid credentials', async ({ page }) => { await page.goto('/login'); await page.getByLabel('Email').fill('reader@example.test'); await page.getByLabel('Password').fill('wrong-password'); await page.getByRole('button', { name: 'Sign in' }).click(); await expect( page.getByRole('alert') ).toHaveText('Email or password is incorrect');});

The labels identify the fields, and the button role plus name identifies the action. The alert assertion checks an observable result and retries until the message appears or the assertion times out. In a real project, arrange an isolated account and avoid relying on shared credentials.

Strictness, chaining, and filtering

Playwright operations that imply one target are strict. If a locator matches two buttons, a click fails instead of choosing silently. Narrow the intent by chaining within a region or filtering a list item by text or another locator.

tests/cart.spec.ts TypeScript
const product = page .getByRole('listitem') .filter({ hasText: 'Desk lamp' }); await product.getByRole('button', { name: 'Add to cart' }).click();

This locator first identifies product rows, narrows them to the row containing the product name, and then finds the action inside that row. It avoids depending on the row’s numeric position.

Risks of first, last, and nth

first(), last(), and nth() can be appropriate when position is the behavior being tested. They are risky when used only to silence a strictness error because a new element can change the position without changing the test code. Prefer a unique description of intent.

CSS and XPath considerations

CSS and XPath are not always wrong. They may be necessary for legacy markup, canvas-adjacent elements, or an interface with no useful accessibility or test contract. Keep them short, avoid deep structural chains, and ask whether improving the application’s labels would also improve accessibility.

Use web-first assertions

Assertions such as toBeVisible() and toHaveText() retry against the locator. A one-time check such as expect(await locator.isVisible()).toBe(true) does not provide the same retry behavior. Waiting should be tied to the expected UI state, not an arbitrary timeout.

Common mistakes

  • Using a deep CSS path that mirrors the current DOM
  • Selecting the first match without explaining why position matters
  • Using placeholder text when a stable label is available
  • Adding a test ID to every element instead of using user-facing semantics
  • Checking visibility once instead of using a retrying assertion
  • Forcing an action before understanding why actionability checks fail

Locator-selection checklist

  1. Start with the way a user or assistive technology identifies the control.
  2. Prefer role and accessible name for interactive elements.
  3. Use labels for form fields.
  4. Use visible text for meaningful content when wording is part of the contract.
  5. Use a deliberate test ID when no dependable user-facing locator exists.
  6. Use CSS or XPath carefully when the application provides no better contract.
  7. Confirm that the locator identifies one intended target and remains readable.

References and further reading

  • LocatorsPlaywright documentation · Verified 2026-07-19
  • Auto-waitingPlaywright documentation · Verified 2026-07-19
  • AssertionsPlaywright documentation · Verified 2026-07-19

Was this article helpful?