Skip to content
Beta · ExperimentalReact 19No peer dependencies

Breadcrumb

Shows where a page sits in your app, collapses long trails, works with your router's links and can publish structured data.

Beta components are subject to change and may break your code. Use them at your own risk, and share feedback through the bug tracker.

On this page

Installation

bash
npx gbs-add-block@latest -a Breadcrumb -beta

The block copies the breadcrumb folder into your project, along with the small shared folder that every component imports. You own the code and can change it freely. There are no peer dependencies other than React.

Requirements

  • React 19 and @types/react 19
  • TypeScript target ES2022 or newer, with "jsx": "react-jsx"
  • Browsers from 2024 or newer (the styles use light-dark() and :dir())

Import the stylesheet once, for example in your global CSS:

css
@import "../components/breadcrumb/styles.css";

or in your root layout / entry file:

ts
import "@/components/breadcrumb/styles.css";

The Breadcrumb shows where the current page sits in your app, and lets people go back up the hierarchy in one click. Long trails collapse to their ends behind an ellipsis. Links can be drawn with your router's link component, such as next/link. The trail can also be published as structured data, so search engines can show it in results.

Set the --gbs-* variables on :root to theme every component at once, the grid included. Each component's own variables fall back to them, and then to the built-in palette, so components look identical out of the box.

Default

Live preview

Quick Start

tsx
"use client";
 
import Link from "next/link";
import { Breadcrumb } from "@/components/breadcrumb";
 
export function InvoiceHeader({ invoice }) {
  return (
    <Breadcrumb
      items={[
        { label: "Home", href: "/" },
        { label: "Invoices", href: "/invoices" },
        { label: invoice.number },
      ]}
      renderLink={(props) => <Link {...props} />}
    />
  );
}

Items go from the top level down. The last item is the current page: it isn't a link, and it's marked aria-current="page".

Props Table

Accepts every <nav> attribute, plus:

PropTypeDefaultDescription
itemsBreadcrumbItem[]requiredThe trail, top level first.
separatorReactNodea chevronBetween crumbs, e.g. "/".
maxItemsnumberCollapse the middle when there are more crumbs than this.
itemsBeforeCollapsenumber1Crumbs kept before the ellipsis.
itemsAfterCollapsenumber1Crumbs kept after the ellipsis, including the current page.
renderLink(props: BreadcrumbLinkProps) => ReactElementan <a>Draw links with your router.
structuredDataboolean | { baseUrl: string }falseAdd schema.org BreadcrumbList JSON-LD. See Structured Data.
size"sm" | "md" | "lg""md"Font size.
aria-labelstringlocaleText.labelName of the navigation landmark.
classNamestringClass for the <nav>.
classNamesPartial<Record<BreadcrumbSlot, string>>Slots: root, list, item, link, current, separator, ellipsis.
localeTextPartial<BreadcrumbLocaleText>EnglishSee Locale Text.
FieldTypeDescription
labelReactNodeText shown. Long labels are cut with an ellipsis and the full text in a tooltip.
hrefstringLink target. Omit for the current page or a crumb that isn't a link.
iconReactNodeIcon before the label, e.g. a home icon.
namestringPlain-text name for structured data, when label isn't a string.
onClick() => voidClick handler, with or without href.

Collapsing Long Trails

tsx
<Breadcrumb items={trail} maxItems={4} itemsBeforeCollapse={1} itemsAfterCollapse={2} />

A 7-crumb trail becomes Home › … › Invoices › INV-042. Pressing the ellipsis reveals the full trail and moves keyboard focus to the first crumb that was hidden. Trails with maxItems crumbs or fewer are never collapsed.

renderLink receives href, className, children and onClick. Spread them onto your link:

tsx
import Link from "next/link";
 
<Breadcrumb items={items} renderLink={(props) => <Link {...props} prefetch={false} />} />;
tsx
// React Router
import { Link } from "react-router";
 
<Breadcrumb items={items} renderLink={({ href, ...props }) => <Link to={href} {...props} />} />;

Structured Data

tsx
<Breadcrumb items={items} structuredData={{ baseUrl: "https://app.example.com" }} />

This adds a <script type="application/ld+json"> with a schema.org BreadcrumbList. Search engines can use it to show the trail under the page title in results. Relative hrefs are resolved against baseUrl, because search engines expect absolute URLs. Render the breadcrumb on the server, as a Next.js page does, so crawlers see the data in the HTML.

Keyboard

KeysAction
TabMove between crumb links and the ellipsis. The current page isn't a stop.
EnterFollow the focused link, or reveal the collapsed crumbs.

Accessibility: the trail is a <nav> landmark named "Breadcrumb", containing an ordered list. Separators are aria-hidden, so screen readers don't read "chevron" between crumbs. The current page has aria-current="page", and the ellipsis says how many crumbs it reveals ("Show 4 more").

Styling and Theming

All rules are in the CSS components layer, so utility classes passed through className / classNames override them.

tsx
<Breadcrumb classNames={{ current: "text-blue-600", separator: "opacity-40" }} />

CSS variables

Override them on .bc-root, on :root, or through style. Each variable falls back to the shared --gbs-* of the same name, then to the DataGrid's --dg-* when that stylesheet is loaded, and finally to the built-in palette.

VariableUsed for
--bc-font-sizeFont size (set by size).
--bc-fgCurrent page and hovered links.
--bc-mutedLinks and separators.
--bc-hoverEllipsis hover background.
--bc-focusFocus ring.
--bc-max-labelLongest label before it's cut (24ch).

Data attributes

ElementAttributes
Root (nav.bc-root)data-size
Item (li.bc-item)data-index
Current page (.bc-current)aria-current="page"

Locale Text

tsx
<Breadcrumb localeText={{ label: "Brotkrumen", showMore: (count) => `${count} weitere anzeigen` }} />
KeyDefault
label"Breadcrumb"
showMore(count) => "Show {count} more"

Headless Use

ExportDescription
collapseItems(count, maxItems?, before?, after?)The rendered trail: crumb indexes and an ellipsis with the hidden ones.
toBreadcrumbJsonLd(items, baseUrl?)The schema.org BreadcrumbList object.

Next.js

The Breadcrumb is a client component with "use client" at the top of its file, and it renders fully on the server, structured data included.

A Server Component can render it directly with plain items (labels, hrefs and icons). renderLink and onClick are functions, and functions can't be passed from a Server Component to a client component. Put those in a small client wrapper instead, and pass it the items from the page:

tsx
// app/components/AppBreadcrumb.tsx
"use client";
 
import Link from "next/link";
import { Breadcrumb, type BreadcrumbItem } from "@/components/breadcrumb";
 
export function AppBreadcrumb({ items }: { items: BreadcrumbItem[] }) {
  return <Breadcrumb items={items} renderLink={(props) => <Link {...props} />} />;
}

Notes

  • In right-to-left layouts, the chevron separator points the other way automatically.
  • There's no previous breadcrumb component, so there's nothing to migrate.