A flexible, framework‑agnostic Vue 3 breadcrumb navigation component with auto-generation from routes, custom separators, max items control, and full customization. Works seamlessly in Single Page Apps or Server-Side Rendered (SSR) environments (e.g. Nuxt 3).
- Features
- Installation
- Quick Start (SPA)
- Nuxt 3 / SSR Usage
- Component Registration Options
- Props
- Events
- Slots
- Auto-Generate Mode
- Max Items & Ellipsis
- Router Integration
- Customization (Styles / Theming)
- Accessibility
- SSR Notes
- Development
- Contributing
- License
- Manual items: Provide a static array of breadcrumb items
- Auto-generation: Automatically generate breadcrumbs from Vue Router routes
- Max items control: Collapse long breadcrumb trails with ellipsis (
…) - Custom separators: Choose your own separator character or component
- Customizable slots: Override item, current item, and separator rendering
- Router integration: Automatically integrates with Vue Router for navigation
- Accessibility: Full ARIA support with semantic HTML and structured data
- SSR-ready: Works in both SPA and SSR (Nuxt 3) contexts
- Lightweight: Tree-shakeable with Vue marked external in library build
Using npm:
npm install @todovue/tv-breadcrumbsUsing yarn:
yarn add @todovue/tv-breadcrumbsUsing pnpm:
pnpm add @todovue/tv-breadcrumbsGlobal registration (main.js / main.ts):
import { createApp } from 'vue'
import App from './App.vue'
import { TvBreadcrumbs } from '@todovue/tv-breadcrumbs'
import '@todovue/tv-breadcrumbs/style.css' // import styles
createApp(App)
.use(TvBreadcrumbs) // enables <TvBreadcrumbs /> globally
.mount('#app')Local import inside a component:
<script setup>
import { TvBreadcrumbs } from '@todovue/tv-breadcrumbs'
const items = [
{ label: 'Home', href: '/' },
{ label: 'Products', href: '/products' },
{ label: 'Category', href: '/products/category' },
{ label: 'Item Details' }
]
function onItemClick({ item, index, event }) {
console.log('Clicked:', item.label)
}
</script>
<template>
<TvBreadcrumbs :items="items" @item-click="onItemClick" />
</template>First, add the stylesheet to your Nuxt config:
// nuxt.config.ts
export default defineNuxtConfig({
modules: [
'@todovue/tv-breadcrumbs/nuxt'
]
})Then create a plugin file: plugins/tv-breadcrumbs.client.ts (or without .client suffix as it's SSR-safe):
import { defineNuxtPlugin } from '#app'
import { TvBreadcrumbs } from '@todovue/tv-breadcrumbs'
export default defineNuxtPlugin(nuxtApp => {
nuxtApp.vueApp.use(TvBreadcrumbs)
})Use anywhere:
<TvBreadcrumbs auto-generate />Optional direct import (no plugin):
<script setup>
import { TvBreadcrumbs } from '@todovue/tv-breadcrumbs'
</script>| Approach | When to use |
|---|---|
Global via app.use(TvBreadcrumbs) |
Many usages across app / design system install |
Local named import { TvBreadcrumbs } |
Isolated / code-split contexts |
Direct default import import { TvBreadcrumbs } from '@todovue/tv-breadcrumbs' |
Single usage or manual registration |
| Prop | Type | Default | Description |
|---|---|---|---|
| items | Array | [] |
Array of breadcrumb items. Each item: { label, href?, disabled?, key? }. |
| separator | String | '›' |
Character or string to display between breadcrumb items. |
| maxItems | Number | 0 |
Maximum items to display. If exceeded, shows first item + ellipsis + last N items. 0 = no limit. |
| autoGenerate | Boolean | false |
Auto-generate breadcrumbs from $route.path or route meta (breadcrumb). |
| homeLabel | String | 'Home' |
Label for the home item when auto-generating breadcrumbs. |
| ariaLabel | String | 'Breadcrumb' |
ARIA label for the <nav> element. |
Each item in the items array can have:
{
label: string // Display text (required)
href?: string // Link URL (optional, null for current page)
disabled?: boolean // Disable interaction (optional)
key?: string // Unique key for rendering (optional, auto-generated if not provided)
}| Event name (kebab) | Payload | Description |
|---|---|---|
item-click |
{ item, index, event } |
Emitted when any breadcrumb item is clicked. |
navigate |
{ to, item, index } |
Emitted when navigation occurs via Vue Router (if router is present). |
Usage:
<TvBreadcrumbs
:items="items"
@item-click="handleClick"
@navigate="handleNavigate"
/>function handleClick({ item, index, event }) {
console.log('Clicked item:', item.label, 'at index:', index)
}
function handleNavigate({ to, item, index }) {
console.log('Navigating to:', to)
}The component provides three slots for full customization:
Customize the rendering of each breadcrumb link (except the last one).
<TvBreadcrumbs :items="items">
<template #item="{ item, index }">
<strong>{{ item.label }}</strong>
</template>
</TvBreadcrumbs>Customize the rendering of the current (last) breadcrumb item.
<TvBreadcrumbs :items="items">
<template #current="{ item, index }">
<em>{{ item.label }}</em>
</template>
</TvBreadcrumbs>Customize the separator between breadcrumb items.
<TvBreadcrumbs :items="items">
<template #separator>
<span> / </span>
</template>
</TvBreadcrumbs>When autoGenerate is enabled, the component automatically creates breadcrumbs from your current route.
<TvBreadcrumbs auto-generate />This reads $route.path and creates breadcrumb items. For example:
- Path:
/docs/guides/installation - Result:
Home › Docs › Guides › Installation
<TvBreadcrumbs auto-generate home-label="Dashboard" />You can define breadcrumbs in your route configuration:
const routes = [
{
path: '/products',
meta: {
breadcrumb: 'Products'
}
},
{
path: '/products/:id',
meta: {
breadcrumb: (route) => [
{ label: 'Products', href: '/products' },
{ label: route.params.id }
]
}
}
]The component will use:
- Route meta
breadcrumbif defined (string, array, or function) - Fallback to auto-generated path segments if no meta found
Control long breadcrumb trails with the maxItems prop:
<TvBreadcrumbs :items="longItemsList" :max-items="4" />Example:
- Original:
Home › Docs › API › v1 › Auth › Scopes - With
max-items="4":Home › … › Auth › Scopes
The algorithm keeps:
- First item (always visible)
- Ellipsis (
…) as a disabled item - Last N-1 items (where N = maxItems)
TvBreadcrumbs automatically detects and integrates with Vue Router:
- Automatic navigation: Clicks on breadcrumb links call
router.push()instead of native navigation - Route reading: Accesses
$routefor auto-generation - Navigate event: Emits when programmatic navigation occurs
Without router: Links work as standard <a> tags with href.
With router: Navigation is handled programmatically, and the navigate event fires.
The component uses scoped styles with BEM-like class names for easy customization:
.tv-breadcrumb-root: Main<nav>container.tv-breadcrumb-list:<ol>list wrapper.tv-breadcrumb-item: Each<li>item.tv-breadcrumb-item--link: Non-current items.tv-breadcrumb-item--current: Current (last) item.tv-breadcrumb-item--disabled: Disabled items
.tv-breadcrumb-link:<a>link element.tv-breadcrumb-current: Current item<span>.tv-breadcrumb-separator: Separator<span>
/* Override default styles */
.tv-breadcrumb-list {
font-size: 14px;
color: #333;
}
.tv-breadcrumb-link {
color: #0066cc;
text-decoration: none;
}
.tv-breadcrumb-link:hover {
text-decoration: underline;
}
.tv-breadcrumb-separator {
margin: 0 8px;
color: #999;
}
.tv-breadcrumb-current {
font-weight: 600;
color: #000;
}TvBreadcrumbs follows WAI-ARIA best practices:
- Semantic HTML: Uses
<nav>,<ol>,<li>for proper structure - ARIA attributes:
aria-labelon<nav>(customizable via prop)aria-current="page"on the current itemaria-disabledon disabled itemsaria-hiddenon separator
- Structured data: Implements Schema.org
ListItemmarkup for search engines - Keyboard navigation: All interactive elements are focusable and keyboard-accessible
- Screen reader friendly: Proper semantic structure and ARIA labels
- No direct DOM (
window/document) access → safe for SSR - Router access is wrapped with safe guards (checks for
$route/$routerexistence) - Works with Nuxt 3 out of the box
- Styles are automatically injected when importing the component
- Auto-generation mode gracefully handles missing router in SSR context
git clone https://github.com/TODOvue/tv-breadcrumbs.git
cd tv-breadcrumbs
npm install
npm run dev # run demo playground
npm run build # build libraryLocal demo served from Vite using index.html + src/demo examples.
PRs and issues welcome. See CONTRIBUTING.md and CODE_OF_CONDUCT.md.
MIT © TODOvue
Crafted for the TODOvue component ecosystem
