# Next.js Bootstrap Breadcrumb

Add a Next.js Bootstrap breadcrumb to App Router pages: static in a server component, or built from usePathname with next/link in a client component.

> This component is only available in **AdminKit PRO**. [Learn more](/pricing/#nextjs).

On a page a few levels deep, such as one invoice or a project's settings, people want to know where they are and how to get back. A breadcrumb answers both. It lists the path from Home to the current page, and every step above the current page is a link.

[AdminKit for Next.js](/nextjs/) has no breadcrumb component: you write Bootstrap's markup in JSX and use `next/link` for the links. Breadcrumbs are part of AdminKit PRO. The stylesheet of the free version leaves out Bootstrap's breadcrumb styles.

Plain markup needs no state, so a breadcrumb with fixed steps works in a server component page, without `"use client"`. A breadcrumb that reads the current URL is different: the hook that gives the URL, `usePathname()`, only works in a client component. Both are below.

## Example

When the steps of a page are known, write them in the page itself. `Link` from `next/link` works in server components, and the page keeps its `metadata` export:

```html
<nav aria-label="breadcrumb">
  <ol class="breadcrumb">
    <li class="breadcrumb-item active" aria-current="page">Home</li>
  </ol>
</nav>

<nav aria-label="breadcrumb">
  <ol class="breadcrumb">
    <li class="breadcrumb-item"><a href="#">Home</a></li>
    <li class="breadcrumb-item active" aria-current="page">Invoices</li>
  </ol>
</nav>

<nav aria-label="breadcrumb">
  <ol class="breadcrumb">
    <li class="breadcrumb-item"><a href="#">Home</a></li>
    <li class="breadcrumb-item"><a href="#">Invoices</a></li>
    <li class="breadcrumb-item active" aria-current="page">Invoice 2041</li>
  </ol>
</nav>
```
```tsx code title="src/app/(dashboard)/invoices/2041/page.tsx"
import Link from "next/link";
import { pageMetadata } from "@/lib/site";

export const metadata = pageMetadata("/invoices/2041");

export default function InvoicePage() {
  return (
    <>
      <nav aria-label="breadcrumb">
        <ol className="breadcrumb">
          <li className="breadcrumb-item">
            <Link href="/">Home</Link>
          </li>
          <li className="breadcrumb-item">
            <Link href="/invoices">Invoices</Link>
          </li>
          <li className="breadcrumb-item active" aria-current="page">
            Invoice 2041
          </li>
        </ol>
      </nav>
      <h1 className="h3 mb-3">Invoice 2041</h1>
    </>
  );
}
```

`pageMetadata()` reads the title and description of a route from `src/data/pages.json`, so a new route needs an entry there.

## Classes

| Class | Element | Effect |
| --- | --- | --- |
| `breadcrumb` | `<ol>` | Lays out the steps in a row, without list bullets |
| `breadcrumb-item` | `<li>` | One step; adds the divider before every step except the first |
| `active` | the last `<li>` | Shows the current page in a muted color; add `aria-current="page"` too |

AdminKit leaves Bootstrap's breadcrumb as it is. For its Sass variables, see [Bootstrap's docs](https://getbootstrap.com/docs/5.3/components/breadcrumb/).

## Breadcrumb from the current route

To give every page a breadcrumb without writing it each time, build the steps from the URL. Each part of the path becomes a step that links to the path up to that part. The labels come from `src/data/pages.json`, the same list that gives each page its title. A path that is not in the list, such as an invoice number, shows the text from the URL.

`usePathname()` from `next/navigation` gives the current path, and the sidebar uses it to highlight the current page. It is a hook, and hooks only run in client components, so this file starts with `"use client"`:

```tsx title="src/components/breadcrumbs.tsx"
"use client";

import Link from "next/link";
import { usePathname } from "next/navigation";
import pages from "@/data/pages.json";

export function Breadcrumbs() {
  const pathname = usePathname();
  const segments = pathname.split("/").filter(Boolean);

  const crumbs = [
    { path: "/", label: "Home" },
    ...segments.map((segment, index) => {
      const path = `/${segments.slice(0, index + 1).join("/")}`;
      const page = pages.find((entry) => entry.path === path);
      return { path, label: page?.title ?? decodeURIComponent(segment) };
    }),
  ];

  return (
    <nav aria-label="breadcrumb">
      <ol className="breadcrumb">
        {crumbs.map((crumb, index) =>
          index === crumbs.length - 1 ? (
            <li key={crumb.path} className="breadcrumb-item active" aria-current="page">
              {crumb.label}
            </li>
          ) : (
            <li key={crumb.path} className="breadcrumb-item">
              <Link href={crumb.path}>{crumb.label}</Link>
            </li>
          )
        )}
      </ol>
    </nav>
  );
}
```

The component looks the titles up in `pages.json` itself. `getPage()` from `@/lib/site` would throw an error for a path that is not in the list.

Render `<Breadcrumbs />` from any page, above its title. A server component page can do that: the component takes no props. A client component still renders on the server first, so the static HTML of each page already contains its breadcrumb. `usePathname()` returns the path without the base path, and `next/link` adds the base path back, so the links also work when the app runs from a sub-path.

The demo's routes are one level deep, such as `/pages-settings`. There, the breadcrumb is Home and the page title. It gets longer once your own routes nest, for example `/invoices/2041`.

## Dividers

The slash between the steps is CSS, not markup. To change it on one breadcrumb, set `--bs-breadcrumb-divider` on the `<nav>`. Its value is a CSS string, with its own quotes:

```html
<nav style="--bs-breadcrumb-divider: '>';" aria-label="breadcrumb">
  <ol class="breadcrumb">
    <li class="breadcrumb-item"><a href="#">Home</a></li>
    <li class="breadcrumb-item active" aria-current="page">Invoices</li>
  </ol>
</nav>
```
```tsx code
<nav
  style={{ "--bs-breadcrumb-divider": "'>'" } as React.CSSProperties}
  aria-label="breadcrumb"
>
  <ol className="breadcrumb">
    <li className="breadcrumb-item">
      <Link href="/">Home</Link>
    </li>
    <li className="breadcrumb-item active" aria-current="page">
      Invoices
    </li>
  </ol>
</nav>
```

The cast is there because TypeScript does not know custom properties in `style`. `"''"` removes the divider. For the whole app, set `$breadcrumb-divider` in `src/scss/1-variables/_app.scss`, which loads before Bootstrap.

## Accessibility

- Wrap the list in a `<nav>` with an `aria-label`, so screen readers announce it as a navigation region.
- Give the last step `aria-current="page"`. The `active` class only changes its color.
- Use an ordered list (`<ol>`): the order of the steps carries meaning.
- The current page is plain text, not a link to itself.

## Related

- [React Bootstrap breadcrumb](/react/docs/navigation/breadcrumb/): the same markup in AdminKit for React (Vite)
- [Bootstrap 5 breadcrumb](/bootstrap/docs/navigation/breadcrumb/): the breadcrumb of the HTML version
- [Navbar](/nextjs/docs/navigation/navbar/): the top bar of every dashboard page
- [Pagination](/nextjs/docs/navigation/pagination/): links between the pages of a long list
- [General UI demo](https://nextjs-demo.adminkit.io/ui-general): breadcrumbs, pagination and other Bootstrap components

---

Canonical HTML page: https://adminkit.io/nextjs/docs/navigation/breadcrumb/

Site overview for agents: https://adminkit.io/llms.txt (expanded: https://adminkit.io/llms-full.txt) · Documentation: https://adminkit.io/bootstrap/docs/ · Questions: support@adminkit.io
