Back to the blogWhat are Playwright locators? Writing tests that survive a redesign
By WonyoungAugust 21, 20266 minOn this page
Before a test can click a button, it has to point at that button. Playwright locators are how it points. Playwright ships seven of them, and which one you pick decides whether your suite keeps running after a redesign or stops all at once. This post covers how to choose among the seven, and the three cases where tests stop anyway.
Playwright locators are seven built-in ways for a test to find an element on the page. Pointing by what users see — role, text, label — survives changes to markup and styling. What remains is deciding whether a change to the UI was intended. No tool answers that.
What "finding an element" means
Ask a person to click the sign-in button and they just do it. They look for the thing that says Sign in and click it.
A machine cannot work from that sentence. To a machine the page is a nested structure of boxes and text, and something has to say which node is the sign-in button. That is what a locator does.
There is more than one way to point at the same button.
- The second element inside the third box from the top
- The element whose class is
btn-primary - The button labeled Sign in
All three point at the same button right now. But the moment the page changes, they part ways. Add a wrapper element and the first one stops. Rename a class during a redesign and the second one stops. The third keeps working as long as the button still says Sign in.
That is exactly the principle Playwright built its locators around.
How you point at an element decides whether it survives a redesign
The seven Playwright locators
The Playwright documentation lists seven built-in locators and recommends prioritizing the ones based on user-facing attributes.
| Locator | Finds by | Where it fits |
|---|---|---|
getByRole | Role and accessible name | Buttons, links, headings, inputs — most things |
getByText | Visible text | Paragraphs, notices |
getByLabel | The label attached to an input | Form fields |
getByPlaceholder | Placeholder text | Inputs without a visible label |
getByAltText | Image alt text | Images |
getByTitle | The title attribute | Elements with tooltips |
getByTestId | A marker the developer planted | When none of the six above can reach it |
The first six share something. They are what a person actually perceives when looking at the page — the words on a button, the label beside a field, the description of an image. They key off meaning rather than structure.
Playwright locators recommend pointing by what users see first
await page.getByRole('button', { name: 'Sign in' }).click();That line does not care where the button sits or which container holds it. If the role is button and the name is Sign in, it finds it.
Why getByRole comes first
Of the seven, Playwright recommends getByRole first. It points using two things: a role and a name.
The role is what the element does on the page — button, link, heading, checkbox — a classification the browser already understands. The name is the human-readable text attached to it.
await expect(page.getByRole('heading', { name: 'Sign up' })).toBeVisible();
await page.getByRole('checkbox', { name: 'Subscribe' }).check();
await page.getByRole('button', { name: /submit/i }).click();There is a side effect worth knowing. Role and name are the same information a screen reader uses to read a page. So an element you cannot reach with getByRole is usually an element assistive technology cannot read either. Writing tests surfaces accessibility problems before anyone files them.
Three cases where tests stop anyway
Good locators do not remove every failure. Three cases come up repeatedly.
1. The text changed. Rename "Sign in" to "Get started" and a locator that pointed by name no longer finds it. That is not the test being wrong. The page genuinely changed, and the test told you. It just told you by failing, which means a person has to look every time.
2. Several elements share a name. A list with ten Delete buttons cannot be resolved by name alone. Narrow the scope instead.
await page.getByRole('listitem')
.filter({ hasText: 'Order 1024' })
.getByRole('button', { name: 'Delete' })
.click();3. The page is not ready yet. While data loads, the button does not exist. Playwright waits for elements to appear by default, so most of this is handled for you. If your page renders in several stages, you still have to decide what to wait on.
Four rules for tests that last
Put together, it comes to this.
- Point by what users see. Text, labels, roles. Markup structure and class names are the last resort.
- Make each locator resolve to exactly one element. If several match, narrow the scope.
- Plant markers only when needed.
getByTestIdis for when the other six cannot reach an element. Tagging every element up front weighs down both the markup and the suite. - When a test stops, look at the page first. Confirm the change was intended before touching the test. Reverse that order and you end up with a passing suite and a broken page.
This is not a Playwright problem
Read this far and locators can look like the source of maintenance cost. They are not.
Lifting element selection up to what users perceive is something Playwright did particularly well. Compared with the older approach of leaning on markup structure, tests stop far less often. Auto-waiting comes from the same idea.
What remains is not the kind of thing a tool can settle. It is deciding whether a change to the page was intended. That decision needs a standard for what the correct state is, and that standard does not live in the code.
Keep readingWhat Is Playwright MCP? 7 Strengths and 3 Ways to Chooseplaywright mcpplaywright8 min
Teams that keep suites alive for years do not stop at writing good locators. When the UI changes they confirm what changed, agree on the new scope, and then update the tests. In that order the suite never drifts into passing for its own sake. The codegen post landed on the same place from a different direction.
Keep readingWhat Is Playwright Codegen? 2 Ways to Get Test Code Writtenplaywright codegenplaywright7 min
Specnote turns that order into a product
Specnote lets people who cannot read code follow the same order. AI lists what it built, a person reviews that list and approves it, and only the approved scope runs in a real browser. No locators to write.
If you can read and edit code, writing them yourself in Playwright is more precise. Follow the rules above and most of the maintenance goes away. What we aim at is the case where nobody has the time or the person to do that.
Technical details in this post were verified against the Playwright documentation on Locators on August 21, 2026.
Frequently asked questions
What are Playwright locators?
They are how a test finds a specific element on the page. Before clicking a button, something has to say which button. Playwright has seven built-in locators that find elements by role, text, label and similar attributes.
Can I use CSS selectors?
You can, and sometimes you need to. CSS selectors lean on markup structure and class names, so they tend to stop when the design is touched. The official docs recommend user-facing attributes first for the same reason.
When should I use getByTestId?
When none of the role, text or label options can reach the element — an icon-only button is the classic case. The developer plants a marker and the test finds it. Tagging everything up front is not recommended.
A test failed. Should I just fix the test?
Check the page first. If you edit the test without checking, you can end up with a passing test and a page that is actually broken. Once that order sets in, the suite stops being worth maintaining.
Can I do e2e testing without reading code?
Writing locators yourself requires reading code. The alternative is a setup where a person approves what should be checked from a list and a tool runs it. That is how Specnote works. > [!CTA] > Approve a list instead of writing locators > > Approve what should be checked once, and every time the UI changes we run that scope in a real browser again. No code to read. > > Start free


