QA How-To
How to Use Playwright getByTitle (2026)
Learn Playwright getByTitle in 2026 with exact and regex matching, metadata, tooltip, accessibility, scoping, frame, and debugging examples for QA teams.
21 min read | 2,729 words
TL;DR
Playwright `getByTitle()` returns a Locator for an element whose HTML `title` attribute matches a string or regex. Use it for intentional supplementary metadata, not as a substitute for accessible control names, visible tooltips, or `expect(page).toHaveTitle()`.
Key Takeaways
- Use getByTitle only for intentional, stable element title attributes.
- Do not confuse getByTitle with the page-level toHaveTitle assertion.
- Prefer role and accessible name for interactive controls.
- Test custom rendered tooltips separately from native title attributes.
- Scope repeated titles to a row, card, toolbar, or region.
- Keep target identity stable when a title changes with component state.
Playwright getByTitle locates elements through their HTML title attribute. It is useful for stable supplementary labels such as an abbreviation explanation, chart marker description, legacy icon metadata, or a component whose title is an intentional UI contract. A basic locator is page.getByTitle('Open settings').
Do not confuse this method with checking the browser tab title, and do not use a title attribute as a substitute for accessible naming. This guide explains the API, matching behavior, runnable patterns, design tradeoffs, and debugging workflow for 2026 Playwright TypeScript suites.
TL;DR
<span title="Estimated delivery date">July 18, 2026</span>
import { test, expect } from '@playwright/test';
test('shows the estimated delivery detail', async ({ page }) => {
await page.goto('/orders/4821');
const delivery = page.getByTitle('Estimated delivery date');
await expect(delivery).toHaveText('July 18, 2026');
});
| Requirement | Correct API | Reason |
|---|---|---|
Find an element by its title attribute |
getByTitle('...') |
Creates a Locator for the element |
| Assert the document title | expect(page).toHaveTitle('...') |
Checks the browser tab title |
| Operate a named button | getByRole('button', { name }) |
Uses accessible semantics |
| Verify visible tooltip content | Locate the rendered tooltip | A title attribute is not the same as a tooltip component |
| Identify a component with no user contract | getByTestId('...') |
Makes test intent explicit |
1. What Playwright getByTitle Matches
getByTitle() creates a Locator for an element whose title attribute matches a string or regular expression. The method can be called from a Page, Locator, or FrameLocator, allowing global queries and queries scoped to a component or frame.
<p>
Service level:
<abbr title="Service Level Agreement">SLA</abbr>
</p>
const abbreviation = page.getByTitle('Service Level Agreement');
await expect(abbreviation).toHaveText('SLA');
Like other Playwright locators, it is lazy. Declaring abbreviation does not save one DOM node forever. Playwright resolves the locator when an assertion, action, or query operation runs. This works well with components that rerender while preserving the title contract.
A title attribute contains supplementary advisory information. Browsers may expose it as a native hover tooltip, but presentation varies by browser and input method. The attribute should not carry critical information that is unavailable elsewhere.
The string matching rules follow Playwright's locator conventions. A plain string uses case-insensitive substring matching. The optional exact: true setting matches the whole normalized title string and is case-sensitive. A regular expression gives explicit control over boundaries, alternatives, and case.
Use getByTitle only when the attribute itself is a meaningful and stable part of the product. A title copied from an implementation library or inserted solely as an accidental side effect is a weak automation contract.
2. Run a Complete getByTitle Test
Create a Playwright project with the official initializer if the repository does not already contain one.
npm init playwright@latest
npx playwright test
The following test reads a titled metadata value, performs an action through an accessible role, and verifies the resulting status.
import { test, expect } from '@playwright/test';
test('refreshes a report with titled metadata', async ({ page }) => {
await page.goto('/reports/quality');
const lastUpdated = page.getByTitle('Last successful refresh');
await expect(lastUpdated).toContainText('July 13, 2026');
await page.getByRole('button', { name: 'Refresh report' }).click();
await expect(page.getByRole('status')).toHaveText('Report refreshed');
await expect(lastUpdated).toContainText('Just now');
});
The title locator identifies metadata whose visible value changes. toContainText provides a retrying content assertion on the same stable Locator after the refresh. The button is located by role because its accessible operation is more important than any supplementary title.
Use a controlled clock or fixture if exact timestamps are under test. Just now is suitable only when the application contract deliberately renders that deterministic state after refresh. A reliable example controls input assumptions rather than adding a wide regex.
Run the focused test in UI mode during development:
npx playwright test tests/report-metadata.spec.ts --ui
For overall project patterns, fixtures, and configuration, see the Playwright testing tutorial.
3. Use Exact and Regular Expression Matching
Matching strategy should reflect how stable the title value is. A substring is concise for a stable phrase inside supplementary detail. Exact matching distinguishes similar titles. A regular expression handles a documented variable suffix or format.
// Matches "Open report details in a new tab".
const details = page.getByTitle('Open report details');
// Matches only this complete normalized value with the same case.
const exactDetails = page.getByTitle('Open report details', {
exact: true,
});
// Allows one of two documented actions.
const panelControl = page.getByTitle(/^(expand|collapse) filters$/i);
Avoid very short strings. getByTitle('Open') may match Open report, Open settings, and Open in new tab. If an action must target one control, strictness will reject multiple matches. Improve the title or use role and accessible name.
For numbered or data-dependent titles, anchor the format:
const issueMarker = page.getByTitle(/^Issue count: \d+$/);
await expect(issueMarker).toHaveText('7');
This verifies a format, not the specific count. When count correctness matters, use controlled data and the exact expected title or visible value.
Whitespace is normalized for text-based locator matching. Do not encode HTML indentation into the expected title. Prefer a string over a regular expression when no controlled variation exists. Readability is part of maintainability, especially when titles already contain punctuation or parentheses that require regex escaping.
4. Do Not Confuse Element Title with Page Title
getByTitle() and toHaveTitle() target different things. The locator method finds an element's title attribute. The page assertion checks the document title displayed in the browser tab and exposed through document.title.
<head>
<title>Order 4821 | QA Shop</title>
</head>
<body>
<span title="Order reference">4821</span>
</body>
await expect(page).toHaveTitle('Order 4821 | QA Shop');
const orderReference = page.getByTitle('Order reference');
await expect(orderReference).toHaveText('4821');
Calling page.getByTitle('Order 4821 | QA Shop') does not target the browser tab title. It searches the rendered document for an element with that title attribute. Likewise, expect(page).toHaveTitle('Order reference') does not inspect the span.
This distinction is a common interview and code-review question because the names look similar. Use the page assertion for navigation, SEO, and document-context tests. Use the locator when supplementary element metadata is the intended contract.
For dynamic document titles, toHaveTitle accepts strings and regular expressions and retries. For element title attributes, getByTitle identifies the node, after which normal locator assertions and actions are available. Clear variable names such as documentTitle and orderReference reduce confusion in longer scenarios.
5. Playwright getByTitle for Abbreviations and Metadata
Abbreviations are a reasonable use of the title attribute when the expanded form is supplementary and also available to users through appropriate product design. A test can verify both the title contract and visible abbreviation.
test('explains report abbreviations', async ({ page }) => {
await page.goto('/reports/help');
const mttr = page.getByTitle('Mean Time to Recovery', { exact: true });
const sla = page.getByTitle('Service Level Agreement', { exact: true });
await expect(mttr).toHaveText('MTTR');
await expect(sla).toHaveText('SLA');
});
Metadata labels are another fit. A visible timestamp may have a title describing its meaning, or a truncated value may expose the full non-critical value.
<span title="Build commit: a84f21c">a84f21c</span>
<span title="Environment: staging">staging</span>
const commit = page.getByTitle(/^Build commit: [a-f0-9]{7}$/);
await expect(commit).toHaveText('a84f21c');
await expect(
page.getByTitle('Environment: staging', { exact: true })
).toBeVisible();
Do not place essential error instructions or consent information only in a title attribute. Native title tooltips can be difficult for keyboard and touch users to discover. Critical content should be visible or exposed through an accessible, operable tooltip or disclosure.
Tests should distinguish metadata correctness from user access. getByTitle proves the attribute value exists. It does not prove every user can discover or read a native tooltip.
6. Icon Controls Need Accessible Names, Not Only Titles
Legacy applications often put a title on an icon button and then locate it with getByTitle. The test may work, but the product contract can still be unclear or inaccessible. Give interactive controls an accessible name and prefer a role locator for the action.
<button type="button" aria-label="Delete invoice" title="Delete">
<svg aria-hidden="true"></svg>
</button>
const deleteInvoice = page.getByRole('button', {
name: 'Delete invoice',
});
await deleteInvoice.click();
A focused metadata test can separately check the title when that hover hint is a requirement:
await expect(page.getByTitle('Delete', { exact: true }))
.toHaveAccessibleName('Delete invoice');
Do not rely on the title attribute as the only label for a critical control. Accessible-name computation can use title as a fallback in some circumstances, but an explicit visible label, aria-label, or aria-labelledby produces clearer intent. Native buttons also preserve keyboard behavior and semantics.
For a visible custom tooltip, locate and test the tooltip component that appears after hover or focus rather than assuming the browser's native title rendering. A product tooltip may use role='tooltip', an ID relationship, and controlled visibility. The Playwright getByRole examples show accessible interaction patterns.
7. Test Custom Tooltips Separately from title Attributes
A title attribute and a custom tooltip are not interchangeable. Native title presentation belongs to the browser and is difficult to style or control. A custom tooltip is application DOM that should be operable and testable through its rendered semantics.
<button aria-describedby="retention-help">Retention policy</button>
<div role="tooltip" id="retention-help" hidden>
Logs are retained for 30 days.
</div>
A component may reveal the tooltip on hover and focus. Test the real interaction:
test('reveals retention help', async ({ page }) => {
await page.goto('/settings/audit');
const trigger = page.getByRole('button', { name: 'Retention policy' });
await trigger.hover();
const tooltip = page.getByRole('tooltip');
await expect(tooltip).toHaveText('Logs are retained for 30 days.');
});
If the component also includes a title as fallback, getByTitle can assert that attribute, but it should not replace the interaction test. Conversely, do not call hover() on a titled element and expect Playwright to query browser chrome for the native tooltip. Browser-native tooltip presentation is not ordinary application DOM.
Keyboard behavior matters. A custom tooltip should appear on focus when required by its design. Use focus() or keyboard navigation in dedicated accessibility coverage and assert the tooltip relationship and content.
This separation prevents a common false conclusion: that locating an element by title proves the user saw a tooltip. It proves only the attribute matched.
8. Scope Repeated Titles to the Correct Component
Repeated icons often share titles such as More options, Information, or Copy. A global locator is ambiguous. Identify the associated row, card, toolbar, or region first.
test('copies the API key from one environment', async ({ page }) => {
await page.goto('/settings/api-keys');
const environment = page.getByRole('row').filter({
has: page.getByRole('cell', { name: 'Staging' }),
});
const copy = environment.getByTitle('Copy API key', { exact: true });
await copy.click();
await expect(page.getByRole('status')).toHaveText('API key copied');
});
If the copy control is a properly named button, role should be the primary locator. This example assumes the existing component's intentional title is its stable contract. A modernization task should add an accessible name and update the test.
For repeated noninteractive metadata, scope the assertion in the same way:
const build = page.getByRole('article').filter({
has: page.getByRole('heading', { name: 'Web application' }),
});
await expect(build.getByTitle('Deployment region'))
.toHaveText('us-east');
Avoid nth() unless order is the behavior being tested. A new environment or card can shift positions. Component scope connects the titled element to stable business identity and produces clearer trace output.
The Playwright locator best practices guide explains how to combine semantic containers, text filters, and explicit hooks without long CSS chains.
9. Handle Dynamic and Stateful Title Values
Some components change their title attribute as state changes, for example Expand filters becoming Collapse filters. If the title itself identifies the state, create separate locators for the before and after values or identify the control stably and assert its attribute.
test('updates a filter panel control', async ({ page }) => {
await page.goto('/search');
const toggle = page.getByRole('button', { name: 'Filters' });
await expect(toggle).toHaveAttribute('title', 'Expand filters');
await toggle.click();
await expect(toggle).toHaveAttribute('aria-expanded', 'true');
await expect(toggle).toHaveAttribute('title', 'Collapse filters');
await expect(page.getByRole('region', { name: 'Filters' }))
.toBeVisible();
});
This approach keeps the target identity stable through the transition. If the test used page.getByTitle('Expand filters') after clicking, that locator should stop matching because the title changed. That may be useful when disappearance of the old title is the exact contract, but it is less clear for multi-step interaction.
Dynamic counts can be validated from controlled data:
const notifications = page.getByTitle('3 unread notifications', {
exact: true,
});
await expect(notifications).toBeVisible();
When the count is not deterministic, use a bounded regex only if the scenario needs presence and format rather than a specific number. Pair it with visible or accessible state so the test does not validate hidden metadata alone.
State should usually be expressed through semantic attributes such as aria-expanded, aria-pressed, checked, or selected. A changing title can supplement that state but should not be its only implementation.
10. Work Across Frames and Shadow DOM
A page locator does not cross an iframe boundary. Select the frame, then call getByTitle in that context.
const analytics = page.frameLocator('iframe[title="Analytics dashboard"]');
const dataPoint = analytics.getByTitle('Conversion rate: 4.2%', {
exact: true,
});
await expect(dataPoint).toBeVisible();
Notice that the iframe itself also has a title attribute. That title names the frame for users and can be selected with a CSS or other appropriate locator when constructing the FrameLocator. The inner getByTitle searches the frame document, not the outer page.
Playwright locators support open shadow roots for normal locator strategies. If a web component renders titled elements inside an open shadow tree, getByTitle can locate them. Closed shadow roots are not traversable. Test those components through public user behavior or an explicit supported interface.
const chart = page.getByTestId('revenue-chart');
await expect(chart.getByTitle('April revenue: $82,000'))
.toBeVisible();
This combines an explicit chart boundary with a titled data point. For rich charts, also validate accessible fallback data or an accompanying table. Title attributes alone are a weak accessibility strategy for critical visual information.
Frame and shadow boundaries are architectural context, not timing problems. Increasing timeouts will not make a page locator cross an iframe or a closed shadow root.
11. Debug Playwright getByTitle Failures
First classify the failure. Zero matches can mean the attribute is absent, spelling or case differs under exact matching, the component has not rendered, the content is inside a frame, or the title was replaced by a custom tooltip implementation. Multiple matches mean the title is generic in the current scope. An actionability failure means a matched element exists but cannot be interacted with as expected.
Use UI mode or the inspector:
npx playwright test tests/metadata.spec.ts --ui
npx playwright test tests/metadata.spec.ts --debug
Inspect the element and confirm that title is an actual attribute, not a React prop consumed without being rendered, an aria-label, or text inside a tooltip node. These contracts look similar in component code but require different locators.
A temporary count can expose duplicates:
const info = page.getByTitle('More information');
console.log('matches:', await info.count());
Remove diagnostics after fixing scope. Do not switch to first() merely to suppress strictness. Identify the associated component, or improve the product markup if every control shares an unhelpful title.
CI traces preserve the DOM snapshot and action timeline. They can show that a new localization changed the title, that server failure prevented the component from rendering, or that a state change updated the attribute before the assertion. Fix the page state or expected contract rather than adding a hard wait.
12. Decide Whether getByTitle Is the Right Contract
Before using this locator, ask what the title means to the product. If it is stable supplementary metadata intentionally rendered on the element, getByTitle is concise and readable. If it is an accidental library detail, a substitute for an accessible label, or volatile helper copy, choose another contract.
| Question | If yes | If no |
|---|---|---|
| Is the title intentional product metadata? | getByTitle may fit | Avoid coupling to it |
| Is the target an interactive control? | Prefer named role for action | Continue evaluating |
| Is critical information only in title? | Improve product design | Supplementary use is safer |
| Are titles repeated? | Scope to business container | Global locator may be unique |
| Does the value change with state? | Use stable target plus attribute assertion | Direct locator may remain stable |
A review checklist should confirm uniqueness, accessibility, scope, state behavior, and the user-visible assertion that follows an action. It should also distinguish an element title from the page title and a custom tooltip.
Use getByTitle sparingly enough that each use communicates something specific. A suite dominated by title locators often signals icon-only controls, missing names, or overreliance on native tooltips. Improve the UI contract rather than standardizing around the symptom.
Interview Questions and Answers
Q: What does Playwright getByTitle locate?
It locates elements by their HTML title attribute using a string or regular expression. It returns a normal Locator with lazy resolution, strictness, and Playwright action and assertion behavior. It does not query the document title.
Q: What is the difference between getByTitle and toHaveTitle?
getByTitle finds an element whose title attribute matches. expect(page).toHaveTitle() asserts the browser tab's document title. They operate on different targets despite similar names.
Q: Is a title attribute an accessible name?
It can participate as a fallback in some accessible-name situations, but it should not be the primary naming strategy for important controls. I prefer visible labels or explicit ARIA naming with native semantics. I test the accessible control through getByRole.
Q: How do you handle repeated More options titles?
I scope the locator to the relevant row, card, toolbar, or region using stable business identity. If the controls are interactive, I also recommend giving each an accessible name that includes context. I avoid positional selection unless order matters.
Q: How do you test a title that changes after a click?
I locate the control through a stable role, name, or test ID and assert its title attribute before and after the action. I also assert semantic state such as aria-expanded and the visible outcome. This keeps target identity stable.
Q: Can getByTitle validate a tooltip?
It validates that a matching title attribute exists. It does not prove that a custom or browser-native tooltip was visible to the user. For a custom tooltip, I trigger hover or focus and assert the rendered tooltip element.
Q: When would you avoid getByTitle?
I avoid it when the title is incidental, frequently edited, generic across many controls, or the only label for critical interaction. Roles, labels, visible text, or deliberate test IDs usually express those contracts more clearly.
Common Mistakes
- Confusing getByTitle with the page-level
toHaveTitleassertion. - Assuming a title attribute proves a visible tooltip appeared.
- Clicking an icon control by title while ignoring its missing accessible name.
- Searching globally for repeated titles such as
More optionsorCopy. - Choosing
first()instead of scoping to the correct row or card. - Using a regex so broad that any title value can satisfy the locator.
- Using a changing title as the only stable identity across a state transition.
- Putting critical instructions only in native title tooltips.
- Expecting a page locator to search an iframe automatically.
- Increasing timeouts when a component stopped rendering the attribute.
- Coupling tests to incidental title strings generated by a UI library.
Correct usage begins with product intent. Confirm that the title is a supported attribute contract, scope it appropriately, preserve accessible interaction, and verify a visible result.
Conclusion
Playwright getByTitle is a focused locator for elements with intentional title attributes. Use plain strings for stable supplementary labels, exact matching for similar titles, and bounded regular expressions for documented variation. Scope repeated titles to meaningful components and keep a stable locator when the title changes with state.
Prefer role and accessible name for interactive controls, toHaveTitle for the browser tab, and rendered tooltip locators for custom help UI. Applied selectively, getByTitle makes metadata tests readable without allowing title attributes to replace sound accessibility or user-visible assertions.
Interview Questions and Answers
What is the purpose of getByTitle?
It locates an element by its HTML title attribute and returns a Playwright Locator. I use it for intentional supplementary metadata, abbreviations, or legacy element contracts. I avoid it when role, label, or visible text better represents user behavior.
How is getByTitle different from toHaveTitle?
`getByTitle()` finds an element with a matching title attribute. `expect(page).toHaveTitle()` checks the document title in the browser tab. Confusing them leads to a zero-match locator or an assertion against the wrong contract.
How do string matching options work?
A plain string is case-insensitive and substring-based. With `exact: true`, Playwright matches the whole normalized string and treats case exactly. A regular expression provides explicit boundaries and case handling.
Is the title attribute sufficient for accessible icon controls?
I do not rely on it as the primary accessible label for critical controls. I use native semantics and an explicit accessible name, then locate the control by role. The title can remain supplementary metadata and have its own focused assertion.
How do you test a dynamic title?
I locate the element through a stable role, name, or test ID and use `toHaveAttribute('title', value)` before and after the state change. I also assert semantic state and the visible outcome. That avoids losing element identity when the title changes.
How do you fix duplicate title matches?
I scope to the relevant row, card, region, or toolbar through business identity. If the title is generic across interactive controls, I improve their accessible names. I use positional selection only for an explicitly ordered requirement.
Can getByTitle prove a tooltip is visible?
No. It proves a matching title attribute exists. A custom tooltip must be triggered through hover or focus and asserted as rendered application content; native browser tooltip presentation is not ordinary DOM content.
Frequently Asked Questions
What does Playwright getByTitle do?
It returns a Locator for elements whose HTML `title` attribute matches the supplied string or regular expression. Normal Locator actions, assertions, strictness, and lazy resolution apply.
Is getByTitle the same as checking the page title?
No. Use `expect(page).toHaveTitle()` for the document title shown in the browser tab. getByTitle searches for an element with a matching title attribute.
How do I make getByTitle exact?
Pass `{ exact: true }` with a string. The whole normalized title value must match, and exact string matching is case-sensitive.
Can getByTitle use a regular expression?
Yes. A regex is useful for documented alternatives or formatted dynamic values, but it should be bounded tightly enough to avoid unrelated matches.
Should I use getByTitle for icon buttons?
Prefer a role locator with an accessible name for interaction. A separate title assertion is reasonable only when the supplementary hover hint is also a product requirement.
Does getByTitle test a tooltip?
It confirms the title attribute exists. For a custom tooltip, trigger its hover or focus behavior and assert the rendered tooltip element and content.
Does getByTitle work inside iframes?
Yes, after entering the correct frame context with `frameLocator()` or a Frame. A page-level locator does not cross iframe boundaries automatically.