---
title: Breadcrumb
description: The trail, told twice from one list: a visible nav with correct ARIA, and the schema.org BreadcrumbList rendered from the same array.
source: https://adamjurek.com/components/breadcrumb
---

## Home


Breadcrumb renders a page's location twice from one array: a visible `<nav>`
trail with correct ARIA, and - when given an `origin` - a schema.org
BreadcrumbList as JSON-LD built from the same items. Use it wherever a page
sits more than one level deep in the site.

<!-- ::start:showcase demo="breadcrumb" height="380" -->
<!-- ::end:showcase -->

## Why it is built this way

The visible trail and the JSON-LD are rendered from the same `items` array on
purpose - it is the only arrangement where the two cannot disagree, and
search engines are explicit that structured data must describe what the page
actually shows. The last crumb renders as text rather than a link: a link to
the page you are already on is the one crumb that does nothing, and a screen
reader announces it as if it did something.

## What it does not do

It renders the JSON-LD only when `origin` is passed - without it, Breadcrumb
is the visible trail alone. Building the item list from a route or a CMS
structure is the caller's job; this component only lays out what it is given.

> [!NOTE] Install commands are not written here
> Anything in `packages/ui/registry.ts` gets its TanStack and shadcn commands
> attached automatically, so there is nothing to keep in sync.


## Install

<!-- ::start:tabs -->

### TanStack

```shell
tanstack add https://adamjurek.com/r/tanstack/breadcrumb.json
```

### shadcn

```shell
pnpm dlx shadcn@latest add https://adamjurek.com/r/shadcn/breadcrumb.json
```

### pnpm

```shell
pnpm add @sushindustries/ui @sushindustries/atoms
```

<!-- ::end:tabs -->

## What you get

| | |
| --- | --- |
| Version | 0.1.0 |
| Category | docs · Navigation |
| Files | `breadcrumb.tsx` |
| Dependencies | None |
| Also installs | `icon` |
| Tags | navigation, seo, json-ld, no-deps |

> [!NOTE] No runtime dependencies
> It brings nothing with it beyond the stylesheet.

## Get Started


Install commands are on Home, attached from the registry - they are not written
here, because a second copy is a copy that goes stale. This tab starts after the
install worked.

## Use it

```tsx
import { Breadcrumb } from "@sushindustries/ui";

const items = [
	{ label: "Components", href: "/components" },
	{ label: "Accordion" },
];

export function Example() {
	return <Breadcrumb items={items} origin="https://sushindustries.com" />;
}
```

## What you should see

"Components" as a link, a chevron separator, then "Accordion" as plain
text with no underline - the last crumb is never a link, since it is the
page already being read. With `origin` set there is also a
`<script type="application/ld+json">` in the output carrying a
`BreadcrumbList`, invisible on the page but present in the source.

## If nothing happens

`Breadcrumb` returns `null` for an empty `items` array rather than an
empty `<nav>` - check the array actually has entries before assuming the
component is broken. Missing structured data in a page's source usually
means `origin` was left unset; it is optional, and without it the
component renders the visible trail only.


## Guides


## Composing it

`items` should end with the current page, and that last entry should
have no `href` - the component treats any item with no `href` as the
current page, not only the last one, so a stray href on what should be
the final crumb turns it back into a link to itself. Root-first order
matters for the JSON-LD as well as the visible trail: `position` in the
structured data comes straight from array index.

## When not to use it

For a page with no real hierarchy above it - a home page, a one-off
landing page - a trail with nothing to show above the current page says
nothing useful. `Breadcrumb` already returns `null` for zero items, but a
single item with no parent to link to is better left out of the page
entirely than rendered as a trail of one.


## API


<!-- generated:api -->

## Props

| Prop | Type | Default | Does |
| --- | --- | --- | --- |
| `items` | `readonly BreadcrumbItem[]` | - | In order, root first. The last item is the current page. |
| `origin?` | `string` | - | Absolute site origin for the JSON-LD `item` URLs. Omit to skip the structured data and render only the visible trail. |

<!-- /generated:api -->

## Notes

`origin` should be the site's own absolute origin, not a per-page value -
anything else produces `item` URLs in the JSON-LD that do not match the
page's real address, which search engines flag as inconsistent
structured data. An item with no `href` renders as the current page
regardless of its position in the array, not only when it is last.


## Examples


Examples are the tab where the component is shown doing a job, not
demonstrating a prop. The API tab already lists the props.

<!-- ::start:showcase demo="breadcrumb" height="420" -->
<!-- ::end:showcase -->

Press Compare. The frames are real viewports, so a layout that breaks at 320
breaks here too rather than in somebody's hands.

## In a page

```tsx
import { Breadcrumb, Hero } from "@sushindustries/ui";

export function ComponentPage({ name }: { name: string }) {
	return (
		<Hero
			trail={
				<Breadcrumb
					items={[
						{ label: "Components", href: "/components" },
						{ label: name },
					]}
					origin="https://sushindustries.com"
				/>
			}
			title={name}
		/>
	);
}
```

## What this example is not

`Breadcrumb` sits inside `Hero`'s `trail` slot here, which is where every
element page on this site puts it - the component itself has no opinion
about placement and renders the same trail whether it sits above a hero,
above an article, or on its own.
