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.
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 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:
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.
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":
"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:
<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 anaria-label, so screen readers announce it as a navigation region. - Give the last step
aria-current="page". Theactiveclass 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: the same markup in AdminKit for React (Vite)
- Bootstrap 5 breadcrumb: the breadcrumb of the HTML version
- Navbar: the top bar of every dashboard page
- Pagination: links between the pages of a long list
- General UI demo: breadcrumbs, pagination and other Bootstrap components
Last updated on
Navbar
The Next.js Bootstrap navbar in AdminKit: notifications, messages, color mode and user menus on every dashboard page, and how to change or add them.
PaginationPro
Next.js Bootstrap pagination for the App Router: a pager in a client component that pages a table, with rows passed in from a server component page.