Shadcn Breadcrumb for React and Tailwind
Shows the current page in a hierarchy.
Installation
bunx --bun shadcn@latest add https://kit.dev/r/breadcrumb.jsonpnpm dlx shadcn@latest add https://kit.dev/r/breadcrumb.jsonnpx shadcn@latest add https://kit.dev/r/breadcrumb.jsonyarn shadcn@latest add https://kit.dev/r/breadcrumb.jsonInstall the following dependencies:
bun add @ark-ui/react lucide-reactpnpm add @ark-ui/react lucide-reactnpm install @ark-ui/react lucide-reactyarn add @ark-ui/react lucide-reactCopy and paste the following code into your project.
"use client";
import { ark } from "@ark-ui/react/factory";
import { ChevronRightIcon, MoreHorizontalIcon } from "lucide-react";
import type React from "react";
import { cn } from "@/lib/utils";
export interface BreadcrumbProps extends React.ComponentProps<typeof ark.nav> {
/**
* Accessible label for the breadcrumb navigation landmark.
*
* @default "Breadcrumb"
*/
"aria-label"?: string;
}
export const Breadcrumb = (props: BreadcrumbProps) => {
const { "aria-label": ariaLabel = "Breadcrumb", ...rest } = props;
return <ark.nav aria-label={ariaLabel} data-slot="breadcrumb" {...rest} />;
};
export const BreadcrumbList = (props: React.ComponentProps<typeof ark.ol>) => {
const { className, ...rest } = props;
return (
<ark.ol
className={cn(
"flex flex-wrap items-center gap-1.5 sm:gap-2.5",
"wrap-break-word text-muted-foreground text-sm",
className
)}
data-slot="breadcrumb-list"
role="list"
{...rest}
/>
);
};
export const BreadcrumbItem = (props: React.ComponentProps<typeof ark.li>) => {
const { className, ...rest } = props;
return (
<ark.li
className={cn("inline-flex items-center gap-1.5", className)}
data-slot="breadcrumb-item"
{...rest}
/>
);
};
export const BreadcrumbLink = (props: React.ComponentProps<typeof ark.a>) => {
const { className, ...rest } = props;
return (
<ark.a
className={cn(
"text-nowrap",
"rounded-md border border-transparent",
"transition-colors",
"hover:text-foreground",
"outline-none focus-visible:border-primary focus-visible:ring-[3px] focus-visible:ring-ring/32 focus-visible:ring-offset-2 focus-visible:ring-offset-background",
"motion-reduce:transition-none!",
className
)}
data-slot="breadcrumb-link"
{...rest}
/>
);
};
export const BreadcrumbPage = (
props: React.ComponentProps<typeof ark.span>
) => {
const { className, ...rest } = props;
return (
<ark.span
aria-current="page"
className={cn("font-normal text-foreground", className)}
data-slot="breadcrumb-page"
{...rest}
/>
);
};
export const BreadcrumbSeparator = (
props: React.ComponentProps<typeof ark.li>
) => {
const { children, className, ...rest } = props;
return (
<ark.li
aria-hidden="true"
className={cn("opacity-64 [&_svg]:size-4", className)}
data-slot="breadcrumb-separator"
role="presentation"
{...rest}
>
{children ?? <ChevronRightIcon className="rtl:rotate-180" />}
</ark.li>
);
};
export const BreadcrumbEllipsis = (
props: React.ComponentProps<typeof ark.span>
) => {
const { className, ...rest } = props;
return (
<ark.span
aria-hidden="true"
className={cn(
"inline-flex size-5 items-center justify-center",
className
)}
data-slot="breadcrumb-ellipsis"
role="presentation"
{...rest}
>
<MoreHorizontalIcon className="size-4" />
</ark.span>
);
};Update the import paths to match your project setup.
Anatomy
BreadcrumbSeparator is a sibling of BreadcrumbItem, not nested inside it. BreadcrumbEllipsis sits inside an item when middle segments are collapsed.
Breadcrumb
└── BreadcrumbList
├── BreadcrumbItem
│ └── BreadcrumbLink | BreadcrumbPage | BreadcrumbEllipsis
└── BreadcrumbSeparatorUsage
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href="/">Home</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbLink href="/components">Components</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>The last segment is BreadcrumbPage (the current page), not a link.
Examples
Custom separator
Pass children to BreadcrumbSeparator. The default is a chevron that flips in RTL.
With links
Use asChild on BreadcrumbLink to render Link (or another router link).
With icon
Icon-only crumbs need aria-label on the link. Decorative icons should be aria-hidden="true".
Collapsed
Replace middle items with BreadcrumbEllipsis. Add a visually hidden label on that item so the gap is announced.
With menu
Compose Menu with the ellipsis so collapsed items stay reachable.
Guides
asChild
asChild comes from Ark’s factory. Merge BreadcrumbLink onto a single child:
<BreadcrumbLink asChild>
<Link href="/docs">Docs</Link>
</BreadcrumbLink>Collapsing long trails
Keep the first crumb, the current page, and one parent. Hide the rest behind BreadcrumbEllipsis. If the ellipsis is not inside a labeled control, put sr-only text on the item.
Separators
Separators are presentational lis (role="presentation", aria-hidden). They do not count as destinations. Default chevron uses rtl:rotate-180. A slash icon does not need to flip.
Breadcrumb vs Tabs vs Bottom Navigation
| Breadcrumb | Tabs | Bottom Navigation | |
|---|---|---|---|
| Role | Where you are in a hierarchy | Panels in one page | Primary app destinations |
| Typical control | Links + current page | Tab triggers | Icon + label items |
API Reference
shadcn.io Breadcrumb is composed nav / ol / li (Ark factory), not a Zag machine. There is no context hook or root provider.
asChild merges props onto a single child element.
Breadcrumb
Landmark. Renders a nav.
| Prop | Type | Default | Description |
|---|---|---|---|
aria-label | string | "Breadcrumb" | Accessible name of the landmark. |
asChild | boolean | false | Render the child element instead of a nav. |
className | string | - | Class names on the root. |
| Attribute | Description |
|---|---|
data-slot | breadcrumb |
aria-label | "Breadcrumb" unless overridden |
BreadcrumbList
Ordered list of segments. Renders an ol.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the list. |
| Attribute | Description |
|---|---|
data-slot | breadcrumb-list |
role | list |
Wraps at flex-wrap. Spacing is gap-1.5 (sm:gap-2.5). Text is text-sm text-muted-foreground.
BreadcrumbItem
One segment. Renders an li.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the item. |
| Attribute | Description |
|---|---|
data-slot | breadcrumb-item |
BreadcrumbLink
Link to an ancestor. Renders an a.
| Prop | Type | Default | Description |
|---|---|---|---|
href | string | - | Destination. Prefer asChild + router Link in Next.js. |
asChild | boolean | false | Merge onto a single child (Link, a). |
className | string | - | Class names on the link. |
Native link attributes (target, rel, aria-label, …) pass through.
| Attribute | Description |
|---|---|
data-slot | breadcrumb-link |
BreadcrumbPage
Current page. Renders a span. Not a link.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the page label. |
| Attribute | Description |
|---|---|
data-slot | breadcrumb-page |
aria-current | page |
BreadcrumbSeparator
Presentational divider between items. Renders an li. Default child is ChevronRightIcon (flips in RTL).
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | chevron | Custom separator. Icons inherit size-4. |
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the separator. |
| Attribute | Description |
|---|---|
data-slot | breadcrumb-separator |
role | presentation |
aria-hidden | true |
BreadcrumbEllipsis
Collapsed-middle marker. Renders a span. Decorative (aria-hidden). Put an accessible name on the parent item or a wrapping button.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the ellipsis. |
| Attribute | Description |
|---|---|
data-slot | breadcrumb-ellipsis |
role | presentation |
aria-hidden | true |
BreadcrumbProps
Props of Breadcrumb: nav attributes plus optional aria-label.
Accessibility
Complies with the Breadcrumb WAI-ARIA pattern. The root is a nav labeled "Breadcrumb". The current page uses aria-current="page" and is not a link.
- Icon-only links:
aria-labelonBreadcrumbLink. Decorative icons:aria-hidden="true". - Ellipsis: keep
aria-hiddenonBreadcrumbEllipsis. Name the wrapping item (sr-only) or button (aria-label). - Separators are hidden from the accessibility tree.
Keyboard support
The trail is not a composite widget. Links participate in the normal tab order.
| Key | Description |
|---|---|
Tab | Move to the next link (or menu trigger). |
Shift + Tab | Move to the previous link. |
Enter | Follow the focused link, or open the ellipsis menu. |
Space | Activate a button ellipsis trigger. |
| Arrow keys | Move inside an open Menu, not along the trail. |