import { Link, useLocation, useMatches } from 'react-router-dom'
interface BreadcrumbMatch {
pathname: string
handle?: {
crumb?: (data?: any) => string
}
data?: any
}
export function Breadcrumbs() {
const location = useLocation()
const matches = useMatches() as BreadcrumbMatch[]
const crumbs = matches
.filter((match) => match.handle?.crumb)
.map((match) => ({
pathname: match.pathname,
label: match.handle!.crumb!(match.data),
}))
if (crumbs.length === 0) return null
return (
<nav aria-label="Breadcrumb" className="mb-4">
<ol className="flex items-center gap-2 text-sm text-gray-600">
<li>
<Link to="/" className="hover:text-gray-900">
<i className="fas fa-home" />
</Link>
</li>
{crumbs.map((crumb, index) => {
const isLast = index === crumbs.length - 1
return (
<li key={crumb.pathname} className="flex items-center gap-2">
<i className="fas fa-chevron-right text-xs text-gray-400" />
{isLast ? (
<span className="font-medium text-gray-900">{crumb.label}</span>
) : (
<Link to={crumb.pathname} className="hover:text-gray-900">
{crumb.label}
</Link>
)}
</li>
)
})}
</ol>
</nav>
)
}
const routes = [
{
path: '/',
element: <Layout />,
children: [
{
path: 'posts',
element: <Posts />,
handle: {
crumb: () => 'Posts',
},
},
{
path: 'posts/:id',
element: <PostDetail />,
handle: {
crumb: (data: any) => data?.post?.title || 'Loading...',
},
loader: async ({ params }) => {
// Load post data for breadcrumb
const post = await fetchPost(params.id!)
return { post }
},
},
],
},
]
Breadcrumbs help users understand their location in deep hierarchies and provide quick navigation to parent pages. I build breadcrumbs from React Router's location state and route configuration. For nested routes, I define metadata like breadcrumb labels in route config and traverse the matched routes to build the breadcrumb trail. Dynamic segments like post IDs can display titles fetched from cache or props. The Link component handles navigation, and I style the current page differently to indicate the active location. Breadcrumbs improve usability and SEO by providing structured navigation links. For mobile, I often show only the immediate parent to save space.