Skip to main content
Vuetify0 v1.0 is here
Vuetify0 Logo
Theme
Mode
Palettes
Accessibility
Vuetify One
Sign in to Vuetify One

Access premium tools across the Vuetify ecosystem — Bin, Play, Studio, and more.

Not a subscriber? See what's included

Breadcrumbs

A headless component for creating responsive breadcrumb navigation with proper ARIA support.

Edit this page
Report a Bug
Open issues
View on GitHub
Copy Markdown

PreviewRenders elementIntermediateJul 28, 2026

Usage

Breadcrumbs renders a navigation trail that shows where the current page sits in a hierarchy. When the trail is wider than its container, the middle crumbs automatically collapse behind an ellipsis so the first crumb and the current page stay visible.

Theme
Mode
Palettes
Accessibility
<script setup lang="ts">
  import { Breadcrumbs } from '@vuetify/v0'
</script>

<template>
  <Breadcrumbs.Root divider="/">
    <Breadcrumbs.List class="flex items-center gap-2 list-none text-sm">
      <Breadcrumbs.Item>
        <Breadcrumbs.Link class="text-primary hover:underline" href="#">
          Home
        </Breadcrumbs.Link>
      </Breadcrumbs.Item>

      <Breadcrumbs.Divider class="text-on-surface-variant" />

      <Breadcrumbs.Ellipsis class="text-on-surface-variant" />

      <Breadcrumbs.Item>
        <Breadcrumbs.Link class="text-primary hover:underline" href="#">
          Products
        </Breadcrumbs.Link>
      </Breadcrumbs.Item>

      <Breadcrumbs.Divider class="text-on-surface-variant" />

      <Breadcrumbs.Item>
        <Breadcrumbs.Link class="text-primary hover:underline" href="#">
          Electronics
        </Breadcrumbs.Link>
      </Breadcrumbs.Item>

      <Breadcrumbs.Divider class="text-on-surface-variant" />

      <Breadcrumbs.Item>
        <Breadcrumbs.Page class="text-on-surface-variant">
          Smartphones
        </Breadcrumbs.Page>
      </Breadcrumbs.Item>
    </Breadcrumbs.List>
  </Breadcrumbs.Root>
</template>

Anatomy

vue
<script setup lang="ts">
  import { Breadcrumbs } from '@vuetify/v0'
</script>

<template>
  <Breadcrumbs.Root>
    <Breadcrumbs.List>
      <Breadcrumbs.Item>
        <Breadcrumbs.Link />
      </Breadcrumbs.Item>

      <Breadcrumbs.Divider />

      <Breadcrumbs.Ellipsis>
        <Breadcrumbs.Activator />
      </Breadcrumbs.Ellipsis>

      <Breadcrumbs.Divider />

      <Breadcrumbs.Item>
        <Breadcrumbs.Page />
      </Breadcrumbs.Item>
    </Breadcrumbs.List>
  </Breadcrumbs.Root>
</template>

Architecture

The Root component composes three internal systems: createBreadcrumbs for navigation state, createGroup for visibility tracking, and createOverflow for width measurement.

Breadcrumbs Architecture

Use controls to zoom and pan. Click outside or press Escape to close.

Breadcrumbs Architecture

createBreadcrumbs manages the navigation model, createGroup tracks which crumbs are visible, and createOverflow measures widths to decide how many fit.

Examples

Responsive Trail with Overflow Collapse

A breadcrumb trail can easily outgrow its container in a sidebar, a resizable panel, or a phone viewport. This example builds a reusable BreadcrumbTrail from a plain path array and lets the Root collapse leading crumbs behind an ellipsis when space runs out. Use the width buttons to shrink the container and watch the middle crumbs fold away while the root and the current page stay put.

The Root composes createOverflow internally: each Breadcrumbs.Item and Breadcrumbs.Divider self-measures, the Root reserves room for the first crumb plus the ellipsis, then hides items from the start using a reverse measurement so the trailing, most-relevant crumbs survive. Crumbs render as Breadcrumbs.Link when they carry an href and as Breadcrumbs.Page (which applies aria-current="page") when they don’t, so the last segment is automatically the current page. The component reads the isOverflowing slot prop off the Root to caption whether the trail collapsed.

Reach for this whenever the number of segments is unknown at design time. The tradeoffs to remember: shrink-0 and whitespace-nowrap keep crumbs from wrapping before the overflow math runs, and the Root gap prop must match your CSS gap or the capacity calculation drifts. For a trail derived from the live route instead of a static array, see the route-derived example below; the navigation model itself comes from createBreadcrumbs.

FileRole
useTrail.tsOwns the demo state — the crumb path array, the container-width presets, and the resize handler
BreadcrumbTrail.vueReusable trail — renders the compound surface, splits Link vs Page on href, and reports overflow via the Root slot prop
breadcrumb-trail.vueEntry — wires the composable to the component and adds the width-preset controls
Container width
Theme
Mode
Palettes
Accessibility

Route-Derived Breadcrumbs

In most applications, breadcrumbs mirror the current URL. Rather than manually maintaining a list of items, this example derives the trail reactively from useRoute()↗︎. When no items prop is provided, AppBreadcrumbs automatically uses the route-derived trail.

File breakdown:

FileRole
useBreadcrumbItems.tsComposable that reads route.path↗︎, splits it into segments, and returns a reactive breadcrumb array
AppBreadcrumbs.vueReusable component — falls back to useBreadcrumbItems when no items prop is provided

Key patterns:

  • The composable wraps its logic in a computed so the trail updates automatically when route.path↗︎ changes during navigation

  • Path segments are title-cased with a simple regex (replace(/\b\w/g, ...))

  • Segments that don’t resolve to a real route (like category folders) render as plain text instead of links

  • The last segment omits href, which causes AppBreadcrumbs to render it as a Breadcrumbs.Page with aria-current="page"

  • Pass explicit items for static trails, or omit the prop to derive from the current route

Navigate to a different page in the docs and watch the breadcrumb trail update.

Theme
Mode
Palettes
Accessibility

Recipes

Common patterns for integrating Breadcrumbs into your application.

Use Breadcrumbs.Link for navigable items and Breadcrumbs.Page for the current (last) item. Page automatically applies aria-current="page".

vue
<template>
  <Breadcrumbs.Item>
    <Breadcrumbs.Link href="/products">Products</Breadcrumbs.Link>
  </Breadcrumbs.Item>

  <!-- Last item uses Page instead of Link -->
  <Breadcrumbs.Item>
    <Breadcrumbs.Page>Current</Breadcrumbs.Page>
  </Breadcrumbs.Item>
</template>

With Vue Router

Use the as prop to render Breadcrumbs.Link as a RouterLink:

vue
<template>
  <Breadcrumbs.Item>
    <Breadcrumbs.Link :as="RouterLink" to="/products">
      Products
    </Breadcrumbs.Link>
  </Breadcrumbs.Item>
</template>

Slot Props

The Root exposes navigation state and methods through its default slot:

vue
<template>
  <Breadcrumbs.Root v-slot="{ isOverflowing, depth, isRoot, first, prev, select }">
    <!-- Use navigation methods and overflow state -->
  </Breadcrumbs.Root>
</template>

Item Gap

The gap prop controls the pixel gap between items used when calculating overflow capacity (default: 8). Adjust it to match your CSS gap so the overflow calculation stays accurate:

vue
<template>
  <!-- CSS gap is 16px — tell the component so overflow math is correct -->
  <Breadcrumbs.Root :gap="16" class="flex gap-4">
    <Breadcrumbs.Item v-for="crumb in crumbs" :key="crumb.path">
      <Breadcrumbs.Link :href="crumb.path">{{ crumb.label }}</Breadcrumbs.Link>
    </Breadcrumbs.Item>
  </Breadcrumbs.Root>
</template>

Custom Ellipsis

Override the ellipsis globally on Root or per-instance:

vue
<template>
  <Breadcrumbs.Root ellipsis="[more]">
    <!-- Ellipsis shows "[more]" instead of default -->
  </Breadcrumbs.Root>
</template>

Plugins

Breadcrumbs integrates with v0’s plugin system for internationalization.

Locale

The Root renders the navigation landmark’s aria-label as ti('Breadcrumbs.label') ?? 'Breadcrumbs'. When the Locale plugin resolves the Breadcrumbs.label key it uses your translation; without any configuration it falls back to the inline English default "Breadcrumbs".

Override with a prop — no plugin needed:

vue
<template>
  <Breadcrumbs.Root label="Fil d'Ariane">
    <!-- ... -->
  </Breadcrumbs.Root>
</template>

Override with the locale plugin — for app-wide i18n:

main.ts
import { createApp } from 'vue'
import { createLocalePlugin } from '@vuetify/v0'
import App from './App.vue'

const app = createApp(App)

app.use(
  createLocalePlugin({
    messages: {
      en: { 'Breadcrumbs.label': 'Breadcrumb' },
      fr: { 'Breadcrumbs.label': "Fil d'Ariane" },
    },
  })
)

app.mount('#app')

The label prop takes priority over locale messages, so you can still override individual instances when needed.

Accessibility

The Breadcrumbs component renders semantic navigation markup and manages ARIA attributes automatically:

  • Breadcrumbs.Root renders a <nav> landmark whose aria-label defaults to "Breadcrumbs" (override with the label prop or the Breadcrumbs.label locale key). When you change the element with as, it applies role="navigation" instead so the landmark is preserved.

  • Breadcrumbs.List renders an ordered list with role="list" to expose the trail as a list to screen readers.

  • Breadcrumbs.Page marks the current item with aria-current="page" so it is announced as the current location. Omitting href on the last crumb renders it as a Page automatically.

  • Breadcrumbs.Link renders a native <a>, so crumbs are focusable and activated with the keyboard like any link — no custom key handling is added.

  • Breadcrumbs.Divider renders aria-hidden="true", keeping the visual separators out of the accessibility tree. Breadcrumbs.Ellipsis does the same while it is purely decorative, and drops it as soon as it hosts a Breadcrumbs.Activator.

  • Breadcrumbs.Item marks collapsed crumbs inert, which removes them from the accessibility tree and from the tab order. aria-hidden alone would leave a collapsed crumb’s link reachable by keyboard while invisible to assistive technology.

Revealing collapsed crumbs

By default a truncated trail gives assistive technology no signal that levels were dropped. Place a Breadcrumbs.Activator inside the ellipsis to turn the truncation into a disclosure:

vue
<template>
  <Breadcrumbs.Root>
    <Breadcrumbs.List>
      <Breadcrumbs.Ellipsis>
        <Breadcrumbs.Activator />
      </Breadcrumbs.Ellipsis>
    </Breadcrumbs.List>
  </Breadcrumbs.Root>
</template>

The ellipsis stays the list item and the Activator is the control, so the trail keeps a valid list structure — button semantics on the <li> would replace its listitem role and break the list’s required children. The Activator renders a native button by default, carrying aria-expanded, a count-aware accessible name, and a data-state of open or closed for styling. Render it as something else with as and it picks up role="button", tabindex, and Enter/Space handling instead.

The disclosure resets on its own once the container grows enough that nothing is truncated.

Locale

The disclosure’s accessible name resolves Breadcrumbs.expand, falling back to Show {count} more breadcrumbs. {count} is how many crumbs truncation hides, and it keeps reporting that total while the disclosure is open so the name stays meaningful.

ts
app.use(
  createLocalePlugin({
    default: 'en',
    messages: {
      en: {
        Breadcrumbs: {
          expand: 'Show {count} more breadcrumbs',
        },
      },
    },
  })
)

For custom implementations, use renderless mode and bind the attrs slot prop to preserve the landmark role and label:

vue
<template>
  <Breadcrumbs.Root v-slot="{ attrs }" renderless>
    <nav v-bind="attrs">
      <!-- Custom breadcrumb trail -->
    </nav>
  </Breadcrumbs.Root>
</template>

FAQ

Discord
Need help? Join our community for support and discussions ↗
Was this page helpful?

© 2016-1970 Vuetify, LLC
Services
Ctrl+/