An XPath that works today can break after a small DOM change: a wrapper div gets added, a class gets renamed, or an index shifts. Long absolute paths and positional indexes are the usual cause.
A sturdier pattern is to anchor the locator to something that rarely changes, such as a visible label or heading, and use an XPath axis to reach the element you need from there.
Fragile:
/html/body/div[2]/div/form/div[3]/input
Anchored:
//label[normalize-space(.)='Email']/following::input[1]
In Playwright:
await page.locator("xpath=//label[normalize-space(.)='Email']/following::input[1]").fill('qa@example.com');
Three axes cover most cases:
-
following::reaches the first matching element after the anchor, like an input after its label. -
following-sibling::reaches a neighbor at the same level, like a button next to a heading. -
ancestor::climbs up to a container, like a card or row, and then lets you search inside it.
The trick is choosing a good anchor: visible text or a stable attribute that a designer is unlikely to change.
Disclosure: I'm the solo developer of Anchor Locator, a Chrome extension that builds axes-based XPath like this. It picks an anchor automatically and lets you choose any element with the Select Anchor button. Generating locators is free. The Recorder exports Playwright, Selenium and Cypress, and its first 10 steps are free. Page data never leaves your browser; only license checks go online.
Chrome Web Store: https://chromewebstore.google.com/detail/blaibbahmbkdkdgkabjlbpgdcldlmadg
Top comments (0)