Breadcrumb
breadcrumbShows a static page hierarchy with a semantic current-page marker.
Usage
Basic usage
Pass in the items array (from the root to the current page). The last item defaults to the current page and cannot be clicked.
<Breadcrumb
items={[
{ label: "Home", href: "/" },
{ label: "Component", href: "/components" },
{ label: "Breadcrumbs" },
]}
/>Custom separator
separator accepts any ReactNode (character or icon), the default is "/", and the separator is automatically added with aria-hidden.
<Breadcrumb items={items} separator={<ChevronIcon />} />Unclickable middle term
href that omits an item is rendered as neutral plain text (non-navigable ancestor) and is still not the current page.
<Breadcrumb
items={[
{ label: "Home", href: "/" },
{ label: "Archive" },
{ label: "2026 Annual Report" },
]}
/>Long path automatic line wrapping
Automatically wrap lines in a narrow container when there are a large number of items, and use the chevron separator to make it clearer.
<div className="max-w-xs">
<Breadcrumb items={longPath} separator={<ChevronIcon />} />
</div>Client-side routing (render slot)
render turns the item into the element you pass (next/link, react-router Link), merging the skin and aria-current into it. It is not click hijacking, so Cmd+click to open a new tab, middle-click and the rest keep working.
<Breadcrumb
items={[
{ label: "Customers", render: <Link href="/customers" /> },
{ label: "Zhang San" }, // the current page omits render, so it stays non-clickable
]}
/>When to use
Use Breadcrumb to show the current page's position in the site hierarchy and provide links back through each ancestor, for example Home / Components / Breadcrumb. Use Tabs to switch peer content in one region, Pagination to move through pages, or Stepper for an ordered process.
Import
import { Breadcrumb } from "@hulianui/ui"Props
| Name | Type | Default | Description |
|---|---|---|---|
| items* | BreadcrumbItem[] | - | Path entries ordered from the root to the current page. |
BreadcrumbItem
| Name | Type | Default | Description |
|---|---|---|---|
| label * | ReactNode | - | Displayed content. |
| href | string | - | Link destination. Omitting it renders a non-interactive item, such as the current page or an ancestor without its own destination. |
| current | boolean | - | Explicitly marks the current page; when no item sets it, the final entry is current. |
| render | ReactElement | - | Render the entry as a custom element (next/link, the react-router Link, and so on). The skin classes and aria-current are merged into that element, and label becomes its children. |
Slots
| Slot | Type | Description |
|---|---|---|
| separator | ReactNode | Separator between entries. Defaults to "/"; alternatives such as a chevron are automatically marked decorative with aria-hidden. |
Example
<Breadcrumb
items={[
{ label: "Home", href: "/" },
{ label: "Components", href: "/components" },
{ label: "Breadcrumb" }, // Final item without href is the current page
]}
/>With a chevron separator:
<Breadcrumb items={items} separator={<ChevronIcon />} />Wiring up a client router (Next.js, react-router) through the same render contract used by Button, Link, and NavMenuItem:
<Breadcrumb
items={[
{ label: "Customers", render: <Link href="/customers" /> },
{ label: "Zhang San" }, // The final entry is the current page; leave render off to keep it non-interactive
]}
/>Usage guidelines
- An intermediate item without
hrefrenders as non-interactive text, which is useful for an ancestor such as Archives that has no standalone page. - Avoid a bare
hrefinside a single-page app, because it triggers a full page load. Pass the framework'sLinkthroughrenderinstead: the element is really rendered rather than having its clicks hijacked on the<nav>, so Cmd+click to open a new tab, middle click, and Shift+click all keep working without hand-written modifier checks. - An entry that sets
renderalways uses it: even the current page renders as that element, only witharia-current="page"added. Leaverenderoff the final entry when the current page should stay non-interactive.hrefnormally comes from the element itself; when the entry also setshref, the entry wins. - Class merge order matches the other
renderslots in the library: the component's own skin classes come first and theclassNameon therenderelement comes last, so the latter wins.
Related
Playground
<Breadcrumb items={items} />