Skip to main content

Overview

While the Meteor component library doesn’t include a dedicated Breadcrumbs component, breadcrumb navigation can be effectively implemented using the Link component (mt-link). This page demonstrates best practices for creating accessible breadcrumb navigation.

Import

import MtLink from '@/components/navigation/mt-link/mt-link.vue';

Creating Breadcrumbs

Basic Breadcrumb Trail

<template>
  <nav aria-label="Breadcrumb">
    <ol class="breadcrumb">
      <li>
        <mt-link to="/">Home</mt-link>
      </li>
      <li aria-hidden="true" class="separator">/</li>
      <li>
        <mt-link to="/products">Products</mt-link>
      </li>
      <li aria-hidden="true" class="separator">/</li>
      <li>
        <mt-link to="/products/electronics">Electronics</mt-link>
      </li>
      <li aria-hidden="true" class="separator">/</li>
      <li aria-current="page">
        <span>Laptops</span>
      </li>
    </ol>
  </nav>
</template>

<script setup>
import MtLink from '@/components/navigation/mt-link/mt-link.vue';
</script>

<style scoped>
.breadcrumb {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  list-style: none;
  padding: 0;
  margin: 0;
}

.separator {
  color: var(--color-text-secondary-default);
}

.breadcrumb li[aria-current="page"] span {
  color: var(--color-text-primary-default);
}
</style>

Dynamic Breadcrumbs from Route

<template>
  <nav aria-label="Breadcrumb">
    <ol class="breadcrumb">
      <li v-for="(crumb, index) in breadcrumbs" :key="crumb.path">
        <template v-if="index < breadcrumbs.length - 1">
          <mt-link :to="crumb.path">{{ crumb.label }}</mt-link>
          <span aria-hidden="true" class="separator">/</span>
        </template>
        <span v-else aria-current="page">{{ crumb.label }}</span>
      </li>
    </ol>
  </nav>
</template>

<script setup>
import { computed } from 'vue';
import { useRoute } from 'vue-router';
import MtLink from '@/components/navigation/mt-link/mt-link.vue';

const route = useRoute();

const breadcrumbs = computed(() => {
  const pathSegments = route.path.split('/').filter(Boolean);
  const crumbs = [{ path: '/', label: 'Home' }];
  
  let currentPath = '';
  pathSegments.forEach((segment, index) => {
    currentPath += `/${segment}`;
    crumbs.push({
      path: currentPath,
      label: segment.charAt(0).toUpperCase() + segment.slice(1)
    });
  });
  
  return crumbs;
});
</script>

<style scoped>
.breadcrumb {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  list-style: none;
  padding: 0;
  margin: 0 0 1rem 0;
}

.separator {
  margin: 0 0.25rem;
  color: var(--color-text-secondary-default);
}
</style>
<template>
  <nav aria-label="Breadcrumb">
    <ol class="breadcrumb">
      <li>
        <mt-link to="/" class="home-link">
          <mt-icon name="regular-home" size="1rem" />
          <span>Home</span>
        </mt-link>
      </li>
      <li>
        <mt-icon name="regular-chevron-right" size="0.75rem" class="separator" aria-hidden="true" />
      </li>
      <li>
        <mt-link to="/dashboard">
          <mt-icon name="regular-chart-line" size="0.875rem" />
          <span>Dashboard</span>
        </mt-link>
      </li>
      <li>
        <mt-icon name="regular-chevron-right" size="0.75rem" class="separator" aria-hidden="true" />
      </li>
      <li aria-current="page">
        <span class="current-page">
          <mt-icon name="regular-cog" size="0.875rem" />
          <span>Settings</span>
        </span>
      </li>
    </ol>
  </nav>
</template>

<script setup>
import MtLink from '@/components/navigation/mt-link/mt-link.vue';
import MtIcon from '@/components/icons-media/mt-icon/mt-icon.vue';
</script>

<style scoped>
.breadcrumb {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  list-style: none;
  padding: 0;
  margin: 0;
}

.home-link,
.current-page {
  display: flex;
  align-items: center;
  gap: 0.375rem;
}

.separator {
  color: var(--color-icon-secondary-default);
}

.current-page {
  color: var(--color-text-primary-default);
}
</style>

Reusable Breadcrumb Component

<!-- Breadcrumbs.vue -->
<template>
  <nav aria-label="Breadcrumb">
    <ol class="breadcrumb">
      <template v-for="(item, index) in items" :key="item.path || index">
        <li>
          <mt-link v-if="index < items.length - 1" :to="item.path">
            {{ item.label }}
          </mt-link>
          <span v-else aria-current="page">{{ item.label }}</span>
        </li>
        <li v-if="index < items.length - 1" aria-hidden="true" class="separator">
          {{ separator }}
        </li>
      </template>
    </ol>
  </nav>
</template>

<script setup>
import MtLink from '@/components/navigation/mt-link/mt-link.vue';

withDefaults(
  defineProps<{
    items: Array<{ path?: string; label: string }>;
    separator?: string;
  }>(),
  {
    separator: '/'
  }
);
</script>

<style scoped>
.breadcrumb {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  list-style: none;
  padding: 0;
  margin: 0;
}

.separator {
  color: var(--color-text-secondary-default);
}
</style>

<!-- Usage -->
<template>
  <Breadcrumbs :items="breadcrumbItems" />
</template>

<script setup>
const breadcrumbItems = [
  { path: '/', label: 'Home' },
  { path: '/products', label: 'Products' },
  { path: '/products/electronics', label: 'Electronics' },
  { label: 'Laptops' } // Current page (no path)
];
</script>
to
string
The target route path
as
string
default:"'router-link'"
The HTML element or component to render as
variant
'primary' | 'critical'
default:"'primary'"
Visual style variant
disabled
boolean
default:"false"
Disables the link
type
'external' | 'internal'
Shows an icon indicating link type

Accessibility Best Practices

  1. Use semantic HTML: Wrap breadcrumbs in <nav> with aria-label="Breadcrumb"
  2. Use ordered list: Breadcrumbs should use <ol> to convey hierarchy
  3. Mark current page: Use aria-current="page" on the last item
  4. Hide decorative elements: Add aria-hidden="true" to separators
  5. Ensure contrast: Make sure link colors meet WCAG standards

Styling Guidelines

  • Keep breadcrumbs compact and horizontal
  • Use subtle separators (/, >, or chevron icons)
  • The current page should not be a clickable link
  • Consider truncating very long breadcrumb trails on mobile
  • Maintain consistent spacing between items

Source

View the Link component source at /home/daytona/workspace/source/packages/component-library/src/components/navigation/mt-link/mt-link.vue:1

Build docs developers (and LLMs) love