# Breadcrumb

> Breadcrumb trail that collapses the middle into an expandable ellipsis.

- Author: Atelier UI
- License: MIT
- Page: https://chromotion.dev/en/components/breadcrumb
- Site: Atelier UI (https://chromotion.dev)

## Install

```bash
npx shadcn@latest add https://chromotion.dev/r/breadcrumb.json
```

Stack: React + TypeScript + Tailwind CSS v4, shadcn/ui conventions. `cn` comes from `@/lib/utils`.

## Dependencies

```bash
npm install lucide-react
```

## Usage

```tsx title="components/breadcrumb-demo.tsx"
import { House } from "lucide-react"

import { Breadcrumb } from "@/components/breadcrumb"

const path = [
  { label: "Home", href: "#", icon: <House className="size-3.5" /> },
  { label: "Workspace", href: "#" },
  { label: "Projects", href: "#" },
  { label: "Aurora", href: "#" },
  { label: "Design system", href: "#" },
  { label: "Buttons" },
]

export default function BreadcrumbDemo() {
  return (
    <div className="flex flex-col gap-6">
      <Breadcrumb items={path} />
      <Breadcrumb items={path.slice(0, 4)} separator="slash" />
      <Breadcrumb items={path} separator="dot" maxItems={3} pill />
    </div>
  )
}
```

## Source

```tsx title="components/breadcrumb.tsx"
"use client"

import * as React from "react"
import { ChevronRight, Ellipsis, Slash } from "lucide-react"

import { cn } from "@/lib/utils"

export interface BreadcrumbItem {
  label: string
  href?: string
  icon?: React.ReactNode
}

export interface BreadcrumbProps extends React.ComponentProps<"nav"> {
  items: BreadcrumbItem[]
  separator?: "chevron" | "slash" | "dot"
  /** Collapse the middle items behind an ellipsis when there are more than this many */
  maxItems?: number
  /** Show items as soft pills */
  pill?: boolean
}

function Separator({ kind }: { kind: NonNullable<BreadcrumbProps["separator"]> }) {
  return (
    <li role="presentation" aria-hidden className="flex items-center text-muted-foreground/60">
      {kind === "chevron" && <ChevronRight className="size-3.5" />}
      {kind === "slash" && <Slash className="size-3 -rotate-12" />}
      {kind === "dot" && <span className="size-1 rounded-full bg-current" />}
    </li>
  )
}

export function Breadcrumb({ items, separator = "chevron", maxItems = 4, pill = false, className, ...props }: BreadcrumbProps) {
  const [expanded, setExpanded] = React.useState(false)
  const collapse = !expanded && items.length > maxItems && maxItems >= 2
  // Keep the first item and the last (maxItems - 1) items
  const visible = collapse
    ? [items[0], null, ...items.slice(items.length - (maxItems - 1))]
    : items

  const link = cn(
    "flex items-center gap-1.5 rounded-md transition-colors",
    pill ? "bg-muted px-2.5 py-1 hover:bg-accent" : "hover:text-foreground"
  )

  return (
    <nav aria-label="Breadcrumb" className={className} {...props}>
      <ol className="flex flex-wrap items-center gap-1.5 text-sm text-muted-foreground">
        {visible.map((item, index) => {
          const last = index === visible.length - 1
          return (
            <React.Fragment key={item ? `${item.label}-${index}` : "ellipsis"}>
              <li className="flex items-center">
                {item === null ? (
                  <button
                    type="button"
                    onClick={() => setExpanded(true)}
                    aria-label={`Show ${items.length - maxItems} hidden items`}
                    className={cn(link, "px-1.5 py-1 hover:bg-accent", pill && "px-2")}
                  >
                    <Ellipsis className="size-4" />
                  </button>
                ) : last ? (
                  <span
                    aria-current="page"
                    className={cn("flex items-center gap-1.5 font-medium text-foreground", pill && "rounded-md bg-primary/10 px-2.5 py-1 text-primary")}
                  >
                    {item.icon}
                    {item.label}
                  </span>
                ) : (
                  <a href={item.href ?? "#"} className={link}>
                    {item.icon}
                    {item.label}
                  </a>
                )}
              </li>
              {!last && <Separator kind={separator} />}
            </React.Fragment>
          )
        })}
      </ol>
    </nav>
  )
}
```
