Skip to main content
Version: 1.3.0 Last Updated: 2026-04-18 Purpose: Comprehensive guide for implementing breadcrumbs in Encore OS Platform
Navigation Documentation Index: For an overview of all navigation documentation, see NAVIGATION_GUIDE_INDEX.md

Overview

Breadcrumbs provide hierarchical navigation context, helping users understand their location within the application and navigate back to parent pages. Encore OS uses a unified breadcrumb system with:
  • Desktop: Auto-generated breadcrumbs in the header (DesktopHeader + Breadcrumbs)
  • Mobile: Same trail as desktop in the fixed context row under the primary MobileHeader row (MobileBreadcrumbs). Do not render a second duplicate trail in page bodies when the shell already shows crumbs.
  • Centralized policy: shouldShowBreadcrumbs / shouldShowMobileHeaderContextRow in src/platform/navigation/utils/breadcrumb-visibility.ts (keep ResponsiveNav main padding-top aligned with whether the context row is visible).
  • Shared segment logic: src/platform/navigation/utils/generate-route-breadcrumb-segments.ts (desktop and mobile use the same generator).
  • Centralized Labels: All route labels in route-labels.ts

Mobile placement contract

  1. When shouldShowBreadcrumbs(pathname) is true, breadcrumbs appear only in MobileHeader’s second row (not inside scrollable main).
  2. PageContainer breadcrumbs + showBreadcrumbs render only when the header policy is false for that route (avoids duplicate chrome). Prefer fixing policy/labels over in-page duplicates.
  3. Two-segment module routes that still need crumbs (e.g. /ce/referral-sources, /cl/pdmp) are allowlisted in breadcrumb-visibility.ts (MODULE_TWO_SEGMENT_BREADCRUMB_ROUTES).
  4. Never mount <Breadcrumbs /> inside page content; use useBreadcrumbLabel / useEntityBreadcrumb for dynamic last segments.

Quick Reference

Basic Implementation

Desktop (Auto-generated):
Mobile:
Custom Route Labels:

Common Patterns

Adding Custom Label:
  1. Open src/platform/navigation/route-labels.ts
  2. Add entry to BASE_ROUTE_LABELS: '/route/path': 'Display Label' (Title Case)
  3. Breadcrumbs automatically use the label
  4. npm run audit:routes-navigation enforces 100% coverage — every <Route> must have a label
Mobile Truncation:
  • Default shows last 2 segments
  • Adjust with maxSegments prop
  • Horizontal scroll for overflow
Component Location:
  • Desktop: @/platform/navigation/Breadcrumbs.tsx
  • Mobile: @/platform/navigation/MobileBreadcrumbs.tsx
  • Labels: @/platform/navigation/route-labels.ts
For detailed architecture and implementation, see sections below.

1. Architecture

Component Structure

Data Flow

  1. User navigates to a route (e.g., /hr/employees/123/edit)
  2. Breadcrumbs.tsx parses the pathname into segments
  3. Each segment is looked up in ROUTE_LABELS
  4. Breadcrumb trail is rendered with links to parent routes

2. Component Reference

2.1 Base Primitives (@/shared/ui/breadcrumb)

2.2 Auto-Generated (@/platform/navigation/Breadcrumbs.tsx)

Automatically generates breadcrumbs based on the current route path. Used in DesktopHeader. Features:
  • Parses pathname into segments
  • Looks up labels in ROUTE_LABELS
  • Handles module context
  • Falls back to formatted path segments

2.3 Mobile (@/platform/navigation/MobileBreadcrumbs.tsx)

Mobile-optimized breadcrumbs with truncation and scroll. Props: Features:
  • Shows last N segments
  • Horizontal scroll for overflow
  • 44px touch targets
  • Safe area support

2.4 Route Labels (@/platform/navigation/route-labels.ts)

Centralized map of route paths to display labels. Keys are full URL paths starting with /. The map currently contains ~1,140 entries covering every declared <Route> in src/routes/*.tsxnpm run audit:routes-navigation enforces this coverage and exits non-zero if any route is missing a label.

2.5 Hub Tab Labels (@/platform/navigation/hub-tab-labels.ts)

For tabbed-hub pages that pass the active tab via ?tab=, the breadcrumb appends the tab label as the trailing segment (e.g. HR > Recruiting > Candidates). The map supports both literal hub paths (/hr/ats) and parameterized paths (/cl/charts/:chartId, /pm/patients/:patientId) via single-segment wildcard matching.

2.6 Dynamic entity labels (@/shared/lib/hooks/useEntityBreadcrumb)

The preferred way to set the trailing crumb to an entity name (e.g. employee full name, invoice number, claim ID) on detail pages.
Under the hood this calls useBreadcrumbLabel(label), which sets the label for the current location.pathname in BreadcrumbContext and clears it on unmount. Both desktop and mobile breadcrumbs then render the entity name in place of the static ROUTE_LABELS value for that path.
Coverage check: npm run audit:breadcrumb-coverage lists detail pages that don’t yet set a dynamic label so they can be migrated. Add new detail pages to this pattern by default.

3. Implementation Patterns

When to use: Most pages where route structure matches breadcrumb trail.
Result: HR > Employees

Pattern 2: Dynamic entity name via useEntityBreadcrumb (preferred for detail pages)

When to use: Detail/edit pages with a :id segment where the trailing crumb should be the entity name (employee, invoice, patient, etc.).
Result: Documents > Meeting Notes 2025-01-15 Recommendation: the page H1 should include the same identifier as the breadcrumb tail. On mobile the breadcrumb truncates to the last 2 segments, so a generic H1 (“Document Details”) leaves the user with no entity context besides the truncated crumb.

Pattern 3: Explicit PageContainer breadcrumbs (fallback)

When to use: Pages that need a custom trail not derivable from the route (e.g. wizards, multi-step flows, two-segment pages outside the auto-show policy). For most routes the header already auto-renders the trail and PageContainer suppresses the inline copy — so prefer Pattern 1 or 2.

⛔ Anti-pattern: importing breadcrumb primitives directly in pages

Do not import @/shared/ui/breadcrumb primitives in core or feature pages. The shadcn primitives are reserved for PageContainer, Breadcrumbs.tsx, and MobileBreadcrumbs.tsx. Pages should only ever rely on:
  1. The auto-generated header trail (Pattern 1)
  2. useEntityBreadcrumb / useBreadcrumbLabel for dynamic entity names (Pattern 2)
  3. PageContainer breadcrumbs={[…]} showBreadcrumbs for custom trails (Pattern 3)

4. Adding New Routes

When creating new features, add route labels to BASE_ROUTE_LABELS in route-labels.ts. Keys are full URL paths starting with /; values are Title Case.

Naming Conventions

Fallback Behavior

If a route is not in ROUTE_LABELS, the system:
  1. Takes the path segment (e.g., work-orders)
  2. Replaces hyphens with spaces
  3. Capitalizes the first letter of each word
  4. Result: “Work Orders”
Always add an explicit label rather than relying on the fallback — audit:routes-navigation enforces 100% coverage.

Two-segment leaf pages (e.g. /ce/referral-sources)

By default shouldShowBreadcrumbs hides crumbs on two-segment module routes (/{core}/{page}), since the ModuleSwitcher already conveys module context. If a particular two-segment leaf page still benefits from the trail (e.g. /ce/referral-sources, /cl/pdmp), add it to MODULE_TWO_SEGMENT_BREADCRUMB_ROUTES in src/platform/navigation/utils/breadcrumb-visibility.ts.

5. Mobile Considerations

MobileBreadcrumbs Integration

Mobile breadcrumbs are automatically included in MobileHeader:

Truncation Behavior

Touch Targets

All breadcrumb links MUST have:
  • Minimum 44x44px touch area
  • Adequate spacing (8px minimum between items)
  • Visual feedback on touch

6. Accessibility

Required Attributes

  • <nav aria-label="Breadcrumb"> on container
  • <ol> for breadcrumb list (semantic ordering)
  • aria-current="page" on current page item

Keyboard Navigation

  • All links must be focusable
  • Tab order follows visual order
  • Focus indicators must be visible

Screen Readers

The breadcrumb structure announces:
  • “Breadcrumb, navigation”
  • Each link in order
  • “Current page: [Page Name]” for last item

7. Testing Checklist

Unit Tests

  • Route parsing generates correct segments
  • ROUTE_LABELS lookup returns expected values
  • Fallback formatting works correctly
  • Module context is handled

Integration Tests

  • Breadcrumbs update on navigation
  • Links navigate to correct routes
  • Mobile truncation works at breakpoints

Accessibility Tests

  • Screen reader announces structure correctly
  • Keyboard navigation works
  • Focus indicators visible

Visual Tests

  • Desktop breadcrumbs render correctly
  • Mobile breadcrumbs truncate properly
  • Touch targets meet 44px requirement
  • Safe areas respected on mobile

8. Common Mistakes & Anti-Patterns

❌ Importing breadcrumb primitives in pages

❌ Detail page without a dynamic label

Run npm run audit:breadcrumb-coverage to see which detail pages still need this migration.

❌ Forgetting to add route labels

❌ Inconsistent label format

❌ Breadcrumbs on module dashboards

❌ Page H1 doesn’t match breadcrumb tail


9. Quick Reference

When to Show Breadcrumbs

File Locations

Audits


Standards

External References