A Locator is a lazy, re-evaluated description of how to find an element. Unlike a Selenium WebElement, which is a handle to one snapshot of the DOM, a Playwright Locator is resolved fresh at the moment of each action, so it never goes stale.
Selenium's findElement returns a WebElement bound to a specific DOM node; if the page re-renders, that reference becomes stale (StaleElementReferenceException). Playwright's page.locator(...) instead stores the query, not the node, and re-runs it on every action — so re-renders, virtual lists and React re-mounts don't break it. Playwright strongly recommends user-facing, semantic locators that mirror how users and assistive technology perceive the page: getByRole, getByLabel, getByPlaceholder, getByText and getByTestId. These are more resilient than brittle CSS/XPath tied to DOM structure. Locators also support chaining and filtering (.filter(), .getByRole(), .nth()), letting you scope within a component instead of writing long absolute paths.
A product card list re-renders when a filter is applied. A Selenium WebElement captured before filtering throws StaleElementReferenceException after. The Playwright locator page.getByRole('listitem').filter({ hasText: 'Wireless Mouse' }).getByRole('button', { name: 'Add to cart' }) keeps working, because it re-resolves against the fresh DOM at click time.
// Resilient, user-facing locators
await page.getByRole('button', { name: 'Sign in' }).click();
await page.getByLabel('Email').fill('qa@qapractice.com');
await page.getByPlaceholder('Search products').fill('mouse');
// Scope within a component with chaining + filter
const card = page.getByRole('listitem').filter({ hasText: 'Wireless Mouse' });
await card.getByRole('button', { name: 'Add to cart' }).click();
// Strictness: this throws if 3 buttons match — be explicit
await page.getByRole('button', { name: 'Delete' }).first().click();
// Last resort — structural locator
await page.locator('#legacy-widget .row:nth-child(2)').click();Scenario: A suite uses XPath like //div[3]/table/tbody/tr[2]/td[4]//button everywhere. It breaks on almost every UI change and reviewers reject the tests as unmaintainable. How do you fix the locator strategy?