Breadcrumbs
A headless component for creating responsive breadcrumb navigation with proper ARIA support.
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.
Anatomy
<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.
createBreadcrumbs manages the navigation model, createGroup tracks which crumbs are visible, and createOverflow measures widths to decide how many fit.
Examples
Recipes
Common patterns for integrating Breadcrumbs into your application.
Links and Current Page
Use Breadcrumbs.Link for navigable items and Breadcrumbs.Page for the current (last) item. Page automatically applies aria-current="page".
<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:
<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:
<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:
<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:
<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:
<template>
<Breadcrumbs.Root label="Fil d'Ariane">
<!-- ... -->
</Breadcrumbs.Root>
</template>Override with the locale plugin — for app-wide i18n:
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.Rootrenders a<nav>landmark whosearia-labeldefaults to"Breadcrumbs"(override with thelabelprop or theBreadcrumbs.labellocale key). When you change the element withas, it appliesrole="navigation"instead so the landmark is preserved.Breadcrumbs.Listrenders an ordered list withrole="list"to expose the trail as a list to screen readers.Breadcrumbs.Pagemarks the current item witharia-current="page"so it is announced as the current location. Omittinghrefon the last crumb renders it as a Page automatically.Breadcrumbs.Linkrenders a native<a>, so crumbs are focusable and activated with the keyboard like any link — no custom key handling is added.Breadcrumbs.Dividerrendersaria-hidden="true", keeping the visual separators out of the accessibility tree.Breadcrumbs.Ellipsisdoes the same while it is purely decorative, and drops it as soon as it hosts aBreadcrumbs.Activator.Breadcrumbs.Itemmarks collapsed crumbsinert, which removes them from the accessibility tree and from the tab order.aria-hiddenalone 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:
<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.
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:
<template>
<Breadcrumbs.Root v-slot="{ attrs }" renderless>
<nav v-bind="attrs">
<!-- Custom breadcrumb trail -->
</nav>
</Breadcrumbs.Root>
</template>FAQ
Use Breadcrumbs.Link for navigable crumbs and Breadcrumbs.Page for the current item — Page applies aria-current="page". Omitting href on the last segment renders it as a Page automatically.
The gap prop (default 8) must match your actual CSS gap. If they differ, the overflow capacity calculation drifts — set :gap to your pixel gap.
Pass :as="RouterLink" along with to on Breadcrumbs.Link.
Set the ellipsis prop on Breadcrumbs.Root (for example ellipsis="[more]") to replace the default overflow indicator, globally or per instance.
Pass a label prop on Breadcrumbs.Root, or configure the Breadcrumbs.label key through the Locale plugin for app-wide i18n. The label prop takes priority over locale messages.