Migrates legacy page-navigation breadcrumbs to @sentry/scraps/breadcrumbList and the required typed title on TopBar.Slot name="breadcrumbs". Use for "migrate breadcrumbs", "replace sentry/components/breadcrumbs", "BreadcrumbList.Title", duplicate page titles, or preserving page filters during breadcrumb migration. Not for event breadcrumbs, issue-detail timeline entries, or a redesign of Settings route assembly.
78
98%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Migrate the requested file or view directory. If no target is supplied, inspect the remaining legacy importers and select a page-navigation call site.
Separate parent links from the current page title. Pass both through one public slot:
// Legacy: one array, including the current page.
<Breadcrumbs crumbs={[{label: t('Monitors'), to: basePath}, {label: monitor.name}]} />
// Current: required typed title, optional parent breadcrumbs.
<TopBar.Slot
name="breadcrumbs"
title={{type: 'page-title', label: monitor.name}}
items={[{type: 'link', label: t('Monitors'), to: basePath}]}
/>For a page without parents, omit items. The title prop is still required.
TopBar.Slot name="breadcrumbs" rejects children. It renders items through BreadcrumbList and internally routes the parents and title to separate outlets. BreadcrumbList.Title supplies the single <h1>. For both title types, only the displayed label is inside the heading; graphics and controls render outside it. An editable title replaces the heading with a labelled input during editing. Do not wrap the title component in another heading. Use it directly only when composing outside TopBar. Do not add a public TopBar.Slot name="title", Layout.Title, or a second page heading; those public title APIs were removed.
Read static/app/views/navigation/topBar.tsx and static/app/components/core/breadcrumbList/ before editing. Prefer the current implementation over old migration examples.
preservePageFilters is not a breadcrumb item prop. A fresh object literal rejects it, but spreading a legacy crumb can compile while silently dropping filters:
// Do not carry legacy fields through a spread.
.map(crumb => ({type: 'link' as const, ...crumb}))Destructure the fields and rebuild the destination. Missing URL filters can clear the destination's project selection; they do not mean "unchanged".
import {extractSelectionParameters} from 'sentry/components/pageFilters/parse';
const to = {
pathname: basePath,
// project, environment, statsPeriod, start, end, utc
query: extractSelectionParameters(location.query),
};When overriding statsPeriod, also clear start and end. When the old destination already has a query, preserve its explicit values:
const destination = {
...oldTo,
query: {...extractSelectionParameters(location.query), ...oldTo.query},
};Keep a bare pathname when the old crumb did not preserve filters. Do not change filter policy as part of migration.
| Surface | Accepted items |
|---|---|
TopBar.Slot.items / BreadcrumbList.items | link, select |
TopBar.Slot breadcrumbs title | page-title, editable-title |
BreadcrumbList.Title item | Same BreadcrumbTitleItem union |
page-title requires a string label. Use labelTooltip, leadingGraphic, pagination, and trailingActions for supporting content.editable-title requires value, onChange, and 'aria-label'.link requires label and either to for internal navigation or externalHref for an external link that opens in a new tab.select accepts options, value, and onChange for parent breadcrumbs only. Settings also uses it for team and integration menus. A separate icon button opens the menu on click. Supply label to retain the name during server search, and leadingGraphic for its icon. search, loading, and onOpenChange pass through to the selector. Supply to to render the label as a navigation link; without it the label is plain text. The current page title cannot be a selector.copy, menu, badge, and button. A selector is a select parent item, not a trailing action.help, badge, href, status, or titleGuide props. Use labelTooltip for help and documentation links, and a trailing badge action for feature badges. Keep the title label plain text.Import public components from @sentry/scraps/breadcrumbList and @sentry/scraps/badge. The breadcrumb barrel exports BreadcrumbList, BreadcrumbListProps, and BreadcrumbTitleItem. Import BreadcrumbListProps directly for parent item types (BreadcrumbListProps['items']); do not infer them with React.ComponentProps<typeof BreadcrumbList>.
rg. Read references/call-site-inventory.md for shared builders and remaining migration shapes.Layout.Title or raw heading before assuming the last crumb is the title.references/title-item.md. Preserve editing, parent selection, navigation, and analytics behavior. Keep the secondary sidebar's feature badge type in the title too.title and items to one TopBar.Slot name="breadcrumbs". Preserve conditions on parent rendering. For branch-dependent titles, let each branch produce a complete slot or title object.Layout.Header or Layout.HeaderContent wrappers left holding only slots. Retain wrappers that still hold tabs or other visible content.pnpm run typecheck, and .venv/bin/prek run -q --files <files>. Read references/tests.md before changing the test harness. Inspect remaining legacy references to verify progress without treating a fixed importer count as a target.For Settings callers, SettingsPageHeader.title accepts a string or BreadcrumbTitleItem, and its breadcrumbs prop accepts additional parent items. Each route can contribute one parent item through handle.settingsBreadcrumb. The shared route layout collects entries from all matched routes in order; children add to their ancestors' entries. Use pathless routes to share a parent among related pages. Use full destination templates, including :orgId, and explicit switchTo destinations for selectors. SettingsBreadcrumbsProvider resolves these declarations and passes items downward through SettingsBreadcrumbsContext. BreadcrumbTitle combines those items with the page's additional items and required title, then renders one TopBar slot. Do not infer destinations from matched paths, register titles upward, or add a fallback title. Keep one explicit title owner per page.
| Open when you need to... | Read |
|---|---|
| Locate remaining callers or trace a shared builder | references/call-site-inventory.md |
| Build titles, selectors, badges, tooltips, editing, or actions | references/title-item.md |
| Check the test harness, queries, or responsive behavior | references/tests.md |
Use views/detectors/components/details/common/header.tsx for slot composition, views/dashboards/dashboardBreadcrumbTitle.tsx for state-dependent titles, views/issueDetails/header/issueIdBreadcrumb.tsx for a typed builder, and views/settings/components/settingsBreadcrumb/settingsBreadcrumbSelector.tsx for parent selectors. Paths are under static/app/.
title and optional items, with no JSX children; no public title slot or Layout.Title remains in the changed code.aria-hidden slot.trailingActions, match the sidebar type, and remain outside leadingGraphic.labelTooltip. Selectors use the select parent item.null. A lone action is a bare object.badge and button actions. Their ReactElement<Props> annotations do not enforce component identity.Keep changes within the requested view area and its shared consumers. Do not add the removed ui-migration-breadcrumbs flag or useHasNewBreadcrumbs hook.
The legacy component's tests and navigation outside the page TopBar can remain. Preserve required navigation landmarks. Retire this skill when no page-navigation migration remains, after inspecting the remaining usages rather than using a fixed file count.
getsentry/sentry
25548ea
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.