Playwright Locators

Master the Built-in Locators

A hands-on reference for every recommended Playwright locator. Each method includes multiple real-world examples with the exact HTML you can target from your tests.

7Locator methods
30+Live examples
1Element ↔ role map

page.getByRole()

Locate elements by their ARIA role and accessible name — the most resilient and strongly recommended locator.

Button by name

button
role.spec.ts
await page.getByRole('button', { name: 'Submit order' }).click();
Live element

Link (exact name)

link
role.spec.ts
await page.getByRole('link', { name: 'Documentation', exact: true }).click();
Live element Documentation

Heading by level

heading
role.spec.ts
await expect(page.getByRole('heading', { level: 3, name: 'Account settings' })).toBeVisible();
Live element

Account settings

Textbox by name

textbox
role.spec.ts
await page.getByRole('textbox', { name: 'Full name' }).fill('Ada Lovelace');
Live element

Searchbox

searchbox
role.spec.ts
await page.getByRole('searchbox', { name: 'Search site' }).fill('locators');
Live element

Checkbox by state

checkbox
role.spec.ts
await page.getByRole('checkbox', { name: 'Subscribe', checked: false }).check();
Live element

Radio in a group

radio
role.spec.ts
await page.getByRole('radio', { name: 'Express shipping' }).check();
Live element
Shipping

Meter (<meter>)

meter
role.spec.ts
await expect(page.getByRole('meter', { name: 'Disk usage' })).toHaveAttribute('value', '6');
Live element

Combobox (select)

combobox
role.spec.ts
await page.getByRole('combobox', { name: 'Country' }).selectOption('India');
Live element

Spinbutton (number)

spinbutton
role.spec.ts
await page.getByRole('spinbutton', { name: 'Quantity' }).fill('3');
Live element

Slider (range)

slider
role.spec.ts
await page.getByRole('slider', { name: 'Volume' }).fill('70');
Live element

Table row & cell

row · cell
role.spec.ts
await page.getByRole('row', { name: 'Ada Lovelace Admin' })
  .getByRole('cell', { name: 'Admin' }).click();
Live element
Name Role
Ada Lovelace Admin
Alan Turing Editor

List & list items

list · listitem
role.spec.ts
await expect(page.getByRole('listitem')).toHaveCount(3);
Live element
  • Playwright
  • Cypress
  • Selenium

Progress bar

progressbar
role.spec.ts
await expect(page.getByRole('progressbar', { name: 'Upload' })).toHaveAttribute('value', '65');
Live element

Disclosure (<summary>)

button
role.spec.ts
await page.getByRole('button', { name: 'Shipping details' }).click();
Live element
Shipping details

Ships in 2–3 business days via standard courier.

Dialog + scoped query

dialog
role.spec.ts
const dialog = page.getByRole('dialog', { name: 'Delete item' });
await dialog.getByRole('button', { name: 'Confirm' }).click();
Live element
Delete item

This action cannot be undone. Are you sure?

Image by name

img
role.spec.ts
await expect(page.getByRole('img', { name: 'Team photo' })).toBeVisible();
Live element Team photo

page.getByText()

Find elements by their text content — substring, exact, or regular expression.

Substring (default)

contains
text.spec.ts
await expect(page.getByText('Welcome back')).toBeVisible();
Live element

Welcome back, Sagar! You have 3 new messages.

Exact match

exact: true
text.spec.ts
await page.getByText('Delete', { exact: true }).click();
Live element Delete Delete all items (won't match)

Regular expression

regex
text.spec.ts
await expect(page.getByText(/\$\d+\.\d{2}/)).toHaveText('$49.99');
Live element $49.99

Filter a list by text

filter
text.spec.ts
await page.getByRole('listitem')
  .filter({ hasText: 'Premium' })
  .getByRole('button').click();
Live element
  • Premium plan
  • Basic plan

page.getByLabel()

Target form controls by their associated <label> or aria-label — the best way to fill forms.

Label linked with for

<label for>
label.spec.ts
await page.getByLabel('Email address').fill('user@example.com');
Live element

Input wrapped by label

implicit
label.spec.ts
await page.getByLabel('Remember me').check();
Live element

Via aria-label

aria-label
label.spec.ts
await page.getByLabel('Card number').fill('4242 4242 4242 4242');
Live element

Exact label match

exact: true
label.spec.ts
await page.getByLabel('Password', { exact: true }).fill('S3cr3t!');
Live element

page.getByPlaceholder()

Locate inputs and textareas that expose a placeholder attribute.

Input by placeholder

input
placeholder.spec.ts
await page.getByPlaceholder('Search the docs').fill('locators');
Live element

Textarea by placeholder

textarea
placeholder.spec.ts
await page.getByPlaceholder('Leave a comment…').fill('Great work!');
Live element

Exact placeholder

exact: true
placeholder.spec.ts
await page.getByPlaceholder('name@work-email.com', { exact: true }).fill('a@b.com');
Live element

page.getByAltText()

Find images (and other elements with alt) by their alternative text.

Image by alt text

<img alt>
alt.spec.ts
await expect(page.getByAltText('Mountain landscape')).toBeVisible();
Live element Mountain landscape

Clickable image by alt

clickable
alt.spec.ts
await page.getByAltText('Product thumbnail').click();
Live element

Alt text with regex

regex
alt.spec.ts
await expect(page.getByAltText(/avatar/i)).toHaveCount(1);
Live element User Avatar

page.getByTitle()

Locate elements by their title attribute (shown as a native tooltip on hover).

Icon button by title

title
title.spec.ts
await page.getByTitle('Close dialog').click();
Live element — hover for tooltip

Text with title tooltip

span
title.spec.ts
await expect(page.getByTitle('Verified account')).toBeVisible();
Live element — hover for tooltip Verified

Link by exact title

exact: true
title.spec.ts
await page.getByTitle('Open settings', { exact: true }).click();
Live element — hover for tooltip Settings

page.getByTestId()

Locate elements by their data-testid — stable IDs that never change with copy or styling.

Element by test id

data-testid
testid.spec.ts
await page.getByTestId('checkout-button').click();
Live element

Scoped inside a container

chained
testid.spec.ts
await page.getByTestId('product-card')
  .getByRole('button', { name: 'Add' }).click();
Live element
Wireless mouse — $19

Custom test-id attribute

config
playwright.config.ts
// playwright.config.ts
export default defineConfig({
  use: { testIdAttribute: 'data-qa' },
});
// test
await page.getByTestId('login-form').isVisible();
Live element

HTML Element ↔ ARIA Role Map

The definitive cheat-sheet: each HTML element, its correct implicit ARIA role, and how to reach it with getByRole().

HTML element Implicit ARIA role getByRole() usage & notes
Landmarks & sectioning
<header> banner Top-level page header. getByRole('banner').
<nav> navigation getByRole('navigation', { name: 'Primary' }).
<main> main One per page. getByRole('main').
<aside> complementary getByRole('complementary').
<footer> contentinfo Top-level footer. getByRole('contentinfo').
<section> region* Only when it has an accessible name (aria-label). getByRole('region', { name }).
<article> article getByRole('article').
<form> form* Role form only when named. getByRole('form', { name }).
Headings & text content
<h1>–<h6> heading getByRole('heading', { level: 1, name }).
<p> paragraph Usually targeted with getByText() instead.
<blockquote> blockquote getByRole('blockquote').
<ul>, <ol> list getByRole('list').
<li> listitem getByRole('listitem').
<dl> / <dt> / <dd> term / definition <dl> itself has no role; <dt>→term, <dd>→definition.
<a href> link getByRole('link', { name }). An <a> with no href has no role.
<strong> strong Rarely queried by role.
<em> emphasis Rarely queried by role.
<code> code getByRole('code').
<img alt> img getByRole('img', { name }) or getByAltText(). Empty alt="" → presentation.
<figure> figure getByRole('figure').
<hr> separator getByRole('separator').
Forms & interactive controls
<button> button getByRole('button', { name }).
<input type="text"> textbox Also email, tel, url map here — except password.
<input type="search"> searchbox getByRole('searchbox', { name }).
<input type="password"> no role Not exposed as textbox. Use getByLabel('Password').
<input type="number"> spinbutton getByRole('spinbutton', { name }).
<input type="checkbox"> checkbox getByRole('checkbox', { name, checked }).
<input type="radio"> radio getByRole('radio', { name }).
<input type="range"> slider getByRole('slider', { name }).
<input type="file"> no role Use setInputFiles() via getByLabel().
<textarea> textbox getByRole('textbox', { name }).
<select> combobox listbox when multiple or size>1. getByRole('combobox', { name }).
<option> option getByRole('option', { name }).
<optgroup> group getByRole('group').
<fieldset> group Named by its <legend>.
<label>, <legend> no role Provide accessible names for other controls.
<output> status Live region. getByRole('status').
<progress> progressbar getByRole('progressbar', { name }).
<meter> meter getByRole('meter', { name }).
Tables
<table> table grid when interactive. getByRole('table').
<caption> caption Names the table.
<thead>, <tbody>, <tfoot> rowgroup getByRole('rowgroup').
<tr> row getByRole('row', { name }).
<th scope="col"> columnheader getByRole('columnheader', { name }).
<th scope="row"> rowheader getByRole('rowheader', { name }).
<td> cell gridcell inside a grid. getByRole('cell').
Disclosure & dialog
<details> group Expandable container.
<summary> button getByRole('button', { name, expanded }).
<dialog> dialog getByRole('dialog', { name }).
ARIA-only roles (no native element — set with role="…")
role="tablist / tab / tabpanel" tab, tablist, tabpanel getByRole('tab', { name, selected }).
role="switch" switch getByRole('switch', { name, checked }).
role="menu / menubar / menuitem" menu, menuitem getByRole('menuitem', { name }).
role="alert / status / log" alert, status, log Live regions. getByRole('alert').
role="tooltip" tooltip getByRole('tooltip').
role="tree / treeitem" tree, treeitem getByRole('treeitem', { name, expanded }).
role="grid / gridcell" grid, gridcell Interactive data grids.

*Conditional role — applies only when the element is named or configured as noted. Common getByRole() options: name, exact, checked, pressed, selected, expanded, level, disabled.