Selector comparison
XPath vs CSS Selector: Which one should you use?
CSS selectors are fast and familiar. XPath is more expressive—parent navigation, text matching, and 100+ functions. Here’s a practical comparison for Selenium and Playwright teams.
Side-by-side comparison
| Dimension | XPath | CSS Selector |
|---|---|---|
| Syntax difficulty | Moderate—functions and axes | Simple—selectors mirror CSS |
| Bidirectional navigation | Yes (parent, ancestor, sibling) | No (only descendants/siblings) |
| Text targeting | Native text() support | Requires :has-text() in Playwright only |
| Attribute logic | Rich functions: contains, starts-with, normalize-space | Exact/contains via *= ^= $= |
| Performance | Slightly slower in large docs; fine for tests | Generally faster in browsers |
| Browser support | document.evaluate in all modern browsers | querySelectorAll everywhere |
| Learning curve | Steeper but powerful | Shallow, familiar to front-end devs |
When XPath wins
- Need to move up the DOM: parent, ancestor, preceding-sibling.
- Text-based matches:
//button[normalize-space()='Submit']. - Complex predicates:
//div[count(p) > 2]or combined conditions. - Working with XML documents (RSS, SVG, configuration files).
When CSS is enough
- Simple attribute or class matches:
.btn.primary. - Native front-end work where querySelectorAll is already in use.
- High-frequency DOM queries where minimal overhead matters.
- Playwright role selectors and CSS fallbacks:
page.getByRole('button', { name: 'Save' }).
Decision guide
- Need to target parent or ancestor? Choose XPath.
- Matching text nodes or combined text + attributes? Choose XPath.
- Simple attribute or class matching with no upward traversal? CSS is faster.
- Prefer Playwright's role/locator API when available; fall back to XPath for complex cases.
- When scraping XML, XPath is the native choice—CSS only works for HTML.
Pro tip:
Start with CSS or role selectors. Switch to XPath when you need text matching, upward traversal, or complex predicates. Validate every selector in the XPath playground before shipping.