Breadcrumbs improve navigation, reduce bounce rates, and can appear as rich snippets in Google search results.
Step 1: Create the Breadcrumb Component
// components/Breadcrumbs.tsx
"use client";
import { usePathname } from "next/navigation";
import Link from "next/link";
interface BreadcrumbItem {
label: string;
href: string;
}
const LABEL_MAP: Record<string, string> = {
services: "Services",
blog: "Blog",
about: "About",
contact: "Contact",
pricing: "Pricing",
industries: "Industries",
locations: "Locations",
};
function generateBreadcrumbs(pathname: string): BreadcrumbItem[] {
const segments = pathname.split("/").filter(Boolean);
return segments.map((segment, index) => ({
label:
LABEL_MAP[segment] ||
segment
.replace(/-/g, " ")
.replace(/\b\w/g, (c) => c.toUpperCase()),
href: "/" + segments.slice(0, index + 1).join("/"),
}));
}
export function Breadcrumbs() {
const pathname = usePathname();
const breadcrumbs = generateBreadcrumbs(pathname);
if (breadcrumbs.length === 0) return null;
return (
<nav aria-label="Breadcrumb" className="mb-6">
<ol className="flex flex-wrap items-center gap-1.5 text-sm text-gray-500 dark:text-gray-400">
<li>
<Link
href="/"
className="transition-colors hover:text-gray-900 dark:hover:text-white"
>
Home
</Link>
</li>
{breadcrumbs.map((crumb, index) => (
<li key={crumb.href} className="flex items-center gap-1.5">
<span aria-hidden="true">/</span>
{index === breadcrumbs.length - 1 ? (
<span className="font-medium text-gray-900 dark:text-white" aria-current="page">
{crumb.label}
</span>
) : (
<Link
href={crumb.href}
className="transition-colors hover:text-gray-900 dark:hover:text-white"
>
{crumb.label}
</Link>
)}
</li>
))}
</ol>
</nav>
);
}
Step 2: Add JSON-LD Schema Markup
// components/BreadcrumbSchema.tsx
interface Props {
items: { name: string; url: string }[];
}
export function BreadcrumbSchema({ items }: Props) {
const schema = {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
itemListElement: items.map((item, index) => ({
"@type": "ListItem",
position: index + 1,
name: item.name,
item: item.url,
})),
};
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
);
}
Step 3: Use in Layout
// app/(site)/layout.tsx
import { Breadcrumbs } from "@/components/Breadcrumbs";
export default function SiteLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="mx-auto max-w-7xl px-4 py-8">
<Breadcrumbs />
{children}
</div>
);
}
Step 4: Server Component Version
For better SEO, create a server component that generates breadcrumbs from page metadata:
// components/ServerBreadcrumbs.tsx
import Link from "next/link";
interface BreadcrumbItem {
label: string;
href: string;
}
export function ServerBreadcrumbs({ items }: { items: BreadcrumbItem[] }) {
const allItems = [{ label: "Home", href: "/" }, ...items];
return (
<>
<nav aria-label="Breadcrumb" className="mb-6">
<ol className="flex flex-wrap items-center gap-1.5 text-sm text-gray-500">
{allItems.map((item, i) => (
<li key={item.href} className="flex items-center gap-1.5">
{i > 0 && <span aria-hidden="true">/</span>}
{i === allItems.length - 1 ? (
<span className="font-medium text-gray-900 dark:text-white" aria-current="page">
{item.label}
</span>
) : (
<Link
href={item.href}
className="hover:text-gray-900 dark:hover:text-white"
>
{item.label}
</Link>
)}
</li>
))}
</ol>
</nav>
<BreadcrumbSchema
items={allItems.map((item) => ({
name: item.label,
url: `https://rcbsoftware.com${item.href}`,
}))}
/>
</>
);
}
Step 5: Use on a Blog Post Page
// app/(site)/blog/[slug]/page.tsx
import { ServerBreadcrumbs } from "@/components/ServerBreadcrumbs";
export default async function BlogPostPage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = getPostBySlug(slug);
return (
<article>
<ServerBreadcrumbs
items={[
{ label: "Blog", href: "/blog" },
{ label: post.title, href: `/blog/${slug}` },
]}
/>
<h1>{post.title}</h1>
{/* post content */}
</article>
);
}
Step 6: Style Variants
// Chevron separator variant
<span aria-hidden="true">
<ChevronRight className="h-3.5 w-3.5" />
</span>
// Truncated long labels
<span className="max-w-[150px] truncate">
{crumb.label}
</span>
// With icons
<li className="flex items-center gap-1.5">
<HomeIcon className="h-3.5 w-3.5" />
<Link href="/">Home</Link>
</li>
Need Better Site Navigation?
We design websites with intuitive navigation, breadcrumbs, and structured data that help users and search engines. Contact us for a free consultation.