Breadcrumbs
zyeon/breadcrumbsFreeComponent
An accessible breadcrumb trail with collapsible overflow — first item + ellipsis + last items once maxItems is exceeded.
Install it
npx shadcn@latest add https://ui.zyeon.ai/r/breadcrumbs.jsonSource
1"use client"23import * as React from "react"4import { ChevronRight, MoreHorizontal } from "lucide-react"5import { cn } from "@/lib/utils"67export interface BreadcrumbItem {8 label: string9 /** Omit on the last (current) item — it renders as text with aria-current="page". */10 href?: string11}1213export interface BreadcrumbsProps extends React.HTMLAttributes<HTMLElement> {14 items: BreadcrumbItem[]15 /** Collapse to first item + ellipsis + last (maxItems - 1) items once items.length exceeds this. 0 disables collapsing. */16 maxItems?: number17 /** Rendered between items and after the ellipsis; aria-hidden. Defaults to a chevron. */18 separator?: React.ReactNode19}2021const DEFAULT_SEPARATOR = <ChevronRight className="size-3.5" />2223type Cell = { kind: "item"; item: BreadcrumbItem } | { kind: "ellipsis" }2425export const Breadcrumbs = React.forwardRef<HTMLElement, BreadcrumbsProps>(26 ({ items, maxItems = 0, separator = DEFAULT_SEPARATOR, className, ...props }, ref) => {27 // One-way expansion: once the ellipsis is pressed it never collapses again, so this28 // state stays internal.29 const [expanded, setExpanded] = React.useState(false)30 const olRef = React.useRef<HTMLOListElement | null>(null)31 // maxItems=1 would collapse the aria-current page itself, so clamp to 2 (first + last).32 const effectiveMax = maxItems > 0 ? Math.max(maxItems, 2) : 033 const collapsed = effectiveMax > 0 && items.length > effectiveMax && !expanded3435 const cells: Cell[] = collapsed36 ? [37 { kind: "item", item: items[0] },38 { kind: "ellipsis" },39 ...items.slice(items.length - (effectiveMax - 1)).map((item): Cell => ({ kind: "item", item })),40 ]41 : items.map((item): Cell => ({ kind: "item", item }))4243 const handleExpand = () => {44 setExpanded(true)45 // The ellipsis button unmounts with the expansion and focus falls to body — move it46 // to the first newly revealed link (anchors[0] is the always-present first crumb,47 // [1] onwards only exist once expanded).48 requestAnimationFrame(() => {49 const anchors = olRef.current?.querySelectorAll("a")50 ;(anchors?.[1] ?? anchors?.[0])?.focus()51 })52 }5354 return (55 <nav aria-label="Breadcrumb" className={cn("text-sm", className)} ref={ref} {...props}>56 <ol className="flex flex-wrap items-center gap-1.5" ref={olRef}>57 {cells.map((cell, index) => {58 const isLast = index === cells.length - 159// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from zyeon
All 893 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| A/B Test Resultab-test-result | zyeon | Components | Paid | 2 deps · 2 files | |
| Accordionaccordion | zyeon | Components | Free | 1 dep | |
| Activity Feedactivity-feed | zyeon | Components | Free | 2 deps · 2 files | |
| Activity Heatmapactivity-heatmap | zyeon | Components | Paid | 1 dep · 2 files | |
| Address Formaddress-form | zyeon | Components | Paid | 1 dep | |
| Agent Graphagent-graph | zyeon | Components | Free | 2 deps · 2 files | |
| Agent Handoffagent-handoff | zyeon | Components | Free | 1 dep | |
| Agent Inboxagent-inbox | zyeon | Components | Free | 2 deps · 2 files |
