Skip to content
Velocity X Docs
← All components

Breadcrumbs

Free · MIT

Static breadcrumb trail with BreadcrumbList microdata that collapses the middle segments into an ellipsis on long paths.

Category
Core UI
Framework
astro
Version
1.0.0
Peer deps
none

Install

bash
velocityx add breadcrumbs

Free and MIT licensed — no account needed. The file lands under src/components/velocity/breadcrumbs/ by default (override with --dest), and it's yours to edit from the moment it's written.

Demo

View the live demo on the showcase site ↗

Source

Pulled live from the registry — this is the exact file velocityx add breadcrumbs writes into your project right now.

components/velocity/breadcrumbs/Breadcrumbs.astro

astro
---
//    VELOCITY COMPONENT · theming
// 
//    Colours here are Tailwind `primary-*` / `accent-*`, which resolve through
//    the Velocity preset. Out of the box they are a neutral indigo — NOT the
//    Aidxn brand — so this drops into your project without dragging our look in.
// 
//    ⚡ NOT USING VELOCITY? Run this once, in your project root:
// 
//          velocityx theme
// 
//    It writes velocity-preset.cjs + velocity-theme.css. Add the preset to your
//    tailwind.config, import the css, then set your whole brand in ONE place:
// 
//          :root {
//            --vx-brand: custom;
//            --vx-primary-600: 220 38 38;   <- your action colour
//            --vx-primary-700: 185 28 28;
//          }
// 
//    ⚠️  Raw RGB channels, space separated — NOT hex. `#DC2626` here silently
//        breaks every /opacity variant in the library (bg-primary-600/20 etc).
// 
//    Prefer not to install anything? Find-and-replace `primary-` with your own
//    scale name. Nothing else in this file depends on Velocity.
/**
 * Breadcrumbs — static trail nav with BreadcrumbList microdata.
 *
 * Collapses the middle on small screens to first + ellipsis + last two,
 * so a deep path never wraps onto three lines on phone.
 *
 * Usage:
 *   <Breadcrumbs items={[{ label: 'Home', href: '/' }, { label: 'Blog', href: '/blog' }, { label: 'Post title' }]} />
 */
export interface Crumb {
  label: string;
  href?: string;
}
export interface Props {
  items: Crumb[];
}
const { items } = Astro.props;
const collapse = items.length > 4;
const head = collapse ? items.slice(0, 1) : items;
const tail = collapse ? items.slice(-2) : [];
---

<nav aria-label="Breadcrumb" class="text-sm">
  <ol class="flex flex-wrap items-center gap-1.5" itemscope itemtype="https://schema.org/BreadcrumbList">
    {head.map((item, i) => (
      <li class="flex items-center gap-1.5" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
        {item.href ? (
          <a href={item.href} itemprop="item" class="text-secondary-500 dark:text-secondary-400 hover:text-primary-700 dark:hover:text-primary-300 rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-600">
            <span itemprop="name">{item.label}</span>
          </a>
        ) : (
          <span aria-current="page" itemprop="name" class="font-medium text-secondary-900 dark:text-white">{item.label}</span>
        )}
        <meta itemprop="position" content={String(i + 1)} />
        {(i < head.length - 1 || collapse) && (
          <svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="text-secondary-300 dark:text-secondary-600">
            <path d="m9 6 6 6-6 6" />
          </svg>
        )}
      </li>
    ))}

    {collapse && (
      <li class="flex items-center gap-1.5 text-secondary-400 dark:text-secondary-500" aria-hidden="true">
        <span>&hellip;</span>
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="text-secondary-300 dark:text-secondary-600">
          <path d="m9 6 6 6-6 6" />
        </svg>
      </li>
    )}

    {tail.map((item, i) => (
      <li class="flex items-center gap-1.5" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
        {item.href ? (
          <a href={item.href} itemprop="item" class="text-secondary-500 dark:text-secondary-400 hover:text-primary-700 dark:hover:text-primary-300 rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-600">
            <span itemprop="name">{item.label}</span>
          </a>
        ) : (
          <span aria-current="page" itemprop="name" class="font-medium text-secondary-900 dark:text-white">{item.label}</span>
        )}
        <meta itemprop="position" content={String(items.length - tail.length + i + 1)} />
        {i < tail.length - 1 && (
          <svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="text-secondary-300 dark:text-secondary-600">
            <path d="m9 6 6 6-6 6" />
          </svg>
        )}
      </li>
    ))}
  </ol>
</nav>

Theming

Uses the shared primary-* / accent-* scale — see Theming if colours look unstyled after install.