apx-dsv0.1Local renderer · live
K
Foundations4
  • Getting started
  • Theming
  • Templates
  • Icons
  • Avatar
  • Badge
  • DataGrid
  • Scheduler
  • Stat
  • Table
  • Timeline
  • TreeView
  • Accordion
  • Alert
  • EmptyState
  • Progress
  • Skeleton
  • Spinner
  • SplashScreen
  • Toast
  • ColorPicker
  • FileUpload
  • Form
  • Rating
  • TagsInput
  • Combobox
  • Field
  • Select
  • Toggle
  • Button
  • Calendar
  • Checkbox
  • DatePicker
  • Input
  • NumberInput
  • Radio
  • Slider
  • Switch
  • Textarea
  • AppShell
  • Div
  • Divider
  • Sidebar
  • Stack
  • Typography
  • Image
  • Breadcrumbs
  • Carousel
  • NavigationMenu
  • Pagination
  • Stepper
  • Tabs
  • Toolbar
  • CommandPalette
  • Confirm
  • Drawer
  • HoverCard
  • Menu
  • Modal
  • Popover
  • Tooltip
  • Icon
  • Card
  • PricingCard
60 componentsapx-ds/renderer
Breadcrumbs
Variant↳ other

Navigation

Breadcrumbs

Navigation-hierarchy primitive. Shows the user\

Overview

<Breadcrumbs> shows the user's path through a nested hierarchy:

tsx
Home / Users / John Smith / Settings
Home / Users / John Smith / Settings

It ships in two flavors that resolve to the same <nav><ol>…</ol></nav> shape:

  • Array API — pass items={[{ label, href }, …]}. Best for dynamic paths from a router.
  • Compound API — <Breadcrumbs.Item> / <Breadcrumbs.Separator> children. Best when items diverge (icons, custom render, mixed link types).

The component supports custom separators, overflow collapse via <Menu>, polymorphic items for router integration, and full RTL.

Overview — product detail path

Loading preview…
Overview.tsx

Anatomy

SubpartElementRole
<Breadcrumbs><nav aria-label="Breadcrumb"> + <ol>container + ordered list of crumbs
<Breadcrumbs.Item><li> wrapping <span> / <a> / asChilda single crumb; current paints non-interactive
<Breadcrumbs.Separator><li role="presentation" aria-hidden>visual divider; SR skips it
Overflow trigger (internal)<li> + <Menu> from apx-dscollapsed-middle dropdown when maxItems is set

Examples

The default example is interactive — every link is clickable, and the truncated example below shows the overflow <Menu> for hidden crumbs.

Basic

Loading preview…
Basic.tsx

Truncated

Loading preview…
Truncated.tsx

CustomSeparator

Loading preview…
CustomSeparator.tsx

WithIcons

Loading preview…
WithIcons.tsx

RouterLink

Loading preview…
RouterLink.tsx

RenderItem

Loading preview…
RenderItem.tsx

Compound

Loading preview…
Compound.tsx

Variants

Loading preview…
Variants.tsx

Sizes

Loading preview…
Sizes.tsx

Colors

Loading preview…
Colors.tsx

Rtl

Loading preview…
Rtl.tsx

LongLabels

Loading preview…
LongLabels.tsx

Props

PropTypeDefaultDescription
childrenReactNode—Children — compound API form. Ignored when `items` is provided.
colorenum——
itemsBreadcrumbsItem[]—Array of crumb items. If omitted, consumers must supply compound `<Breadcrumbs.Item>` children.
itemsAfterCollapsenumber1Items at the end to keep when collapsing.
itemsBeforeCollapsenumber1Items at the start to keep when collapsing.
maxItemsnumber—Maximum number of items to display before collapsing the middle into an overflow menu.
overflowAriaLabelstring—Accessible label for the overflow menu trigger. Defaults to "Show hidden navigation items". Will be replaced by `<I18nProvider>` translations once the i18n primitive ships.
renderItem((ctx: BreadcrumbsRenderItemContext) => ReactNode)—Custom render fn for every item; overrides default link/span rendering.
separatorReactNode'/'Separator node between items. String or React element.
separatorColorenum'muted'Separator paint role.
sizeResponsiveValue<BreadcrumbsSize>——
sxSx—Theme-aware inline style object.
variantResponsiveValue<BreadcrumbsVariant>——

Truncation / overflow

When items.length > maxItems, the middle items collapse into a single <Menu> trigger (visualized as an ellipsis button). itemsBeforeCollapse / itemsAfterCollapse (default 1 each) control how many items remain on each end.

The overflow dropdown is rendered through the same <Menu> primitive that powers the rest of the DS — no custom dropdown logic lives in Breadcrumbs. Clicking a hidden crumb navigates to its href / to; consumers using a router should provide renderItem so each hidden crumb renders inside the menu as their router-aware element.

Accessibility

  • Root is <nav aria-label="Breadcrumb"> (override via aria-label prop). The <ol> inside preserves order semantics — screen readers announce "1 of N", "2 of N", etc.
  • Each crumb is an <li> wrapping its content (span for current / asChild for link).
  • Last item (or any item with current={true} / no href) carries aria-current="page".
  • Separators have role="presentation" + aria-hidden="true" so SRs don't read "slash slash slash" between every item.
  • Overflow trigger is a <button aria-label="Show hidden navigation items" aria-haspopup="menu">.
  • Keyboard: native Tab through link items; the overflow Menu uses Menu's existing keyboard pattern (Enter / arrows / type-ahead).
  • axe-core: zero violations across the variant × color matrix + overflow + compound API cells.

Router integration

tsx
import Link from 'next/link';

<Breadcrumbs>
  <Breadcrumbs.Item asChild>
    <Link href="/">Home</Link>
  </Breadcrumbs.Item>
  <Breadcrumbs.Item asChild>
    <Link href="/users">Users</Link>
  </Breadcrumbs.Item>
  <Breadcrumbs.Item current>John Smith</Breadcrumbs.Item>
</Breadcrumbs>;
import Link from 'next/link';

<Breadcrumbs>
  <Breadcrumbs.Item asChild>
    <Link href="/">Home</Link>
  </Breadcrumbs.Item>
  <Breadcrumbs.Item asChild>
    <Link href="/users">Users</Link>
  </Breadcrumbs.Item>
  <Breadcrumbs.Item current>John Smith</Breadcrumbs.Item>
</Breadcrumbs>;

For dynamic paths, the array API plus renderItem is usually cleaner:

tsx
<Breadcrumbs
  items={crumbsFromRouter}
  renderItem={({ item, isCurrent, defaultClassName }) =>
    isCurrent ? (
      <span aria-current="page" className={defaultClassName}>
        {item.label}
      </span>
    ) : (
      <Link href={item.href ?? item.to ?? '#'} className={defaultClassName}>
        {item.label}
      </Link>
    )
  }
/>
<Breadcrumbs
  items={crumbsFromRouter}
  renderItem={({ item, isCurrent, defaultClassName }) =>
    isCurrent ? (
      <span aria-current="page" className={defaultClassName}>
        {item.label}
      </span>
    ) : (
      <Link href={item.href ?? item.to ?? '#'} className={defaultClassName}>
        {item.label}
      </Link>
    )
  }
/>

Theming

Four override slots are exposed: root, list, item, separator, overflowTrigger. Each is merged through the same useThemedClasses precedence chain every DS component uses.

tsx
<ThemeProvider
  theme={defineTheme({
    components: {
      Breadcrumbs: {
        defaultProps: { variant: 'soft', color: 'primary' },
        styleOverrides: {
          list: 'rounded-md bg-bg-subtle px-2 py-1',
          item: 'font-medium',
          separator: 'opacity-50',
        },
      },
    },
  })}
>
  {children}
</ThemeProvider>
<ThemeProvider
  theme={defineTheme({
    components: {
      Breadcrumbs: {
        defaultProps: { variant: 'soft', color: 'primary' },
        styleOverrides: {
          list: 'rounded-md bg-bg-subtle px-2 py-1',
          item: 'font-medium',
          separator: 'opacity-50',
        },
      },
    },
  })}
>
  {children}
</ThemeProvider>

RTL

  • The <ol> flows right-to-left automatically inside dir="rtl".
  • Long-label truncation uses logical-CSS truncate + max-w-* — works in both directions.
  • Chevron separators should use a logical icon (<ChevronEnd />) so they flip; symmetric separators like / or · need no change.

Do / Don't

  • Do use the array API for dynamic paths from your router.
  • Do put aria-current="page" on the destination crumb (the array API does this for you when the last item has no href).
  • Do use maxItems to keep deep paths inside narrow containers.
  • Don't wrap individual crumbs in a separate <nav> — the root already handles that.
  • Don't put critical actions in a breadcrumb — it's a navigation indicator, not a toolbar.