
Posted on • Originally published atkulpinski.dev
How to Build Dynamic Breadcrumbs in Remix
In Remix, building dynamic breadcrumbs that reflect your route hierarchy is straightforward. This tutorial will guide you through leveraging theuseMatches
andhandle
capabilities to achieve this.
We'll also cover how to add schema metadata to your breadcrumbs for better SEO and social sharing.
The Basics
Breadcrumbs are a navigation aid that helps users understand their current location within a website. They typically appear horizontally at the top of a page and show the hierarchy of the current page in relation to the site structure.
Here's an example of what breadcrumbs code could look like:
<olitemscopeitemtype="https://schema.org/BreadcrumbList"><liitemprop="itemListElement"itemscopeitemtype="https://schema.org/ListItem"><ahref="https://example.com/"itemprop="item"><spanitemprop="name">Home</span></a><metaitemprop="position"content="1"/></li><liitemprop="itemListElement"itemscopeitemtype="https://schema.org/ListItem"><ahref="https://example.com/posts"itemprop="item"><spanitemprop="name">Posts</span></a><metaitemprop="position"content="2"/></li><liitemprop="itemListElement"itemscopeitemtype="https://schema.org/ListItem"><ahref="https://example.com/posts/slug"itemprop="item"><spanitemprop="name">Post Title</span></a><metaitemprop="position"content="3"/></li></ol>
Now, let's see how to build dynamic breadcrumbs in Remix.
Defining the Breadcrumbs Components
Let's start by creating the necessary component that we'll later use to render the breadcrumbs.
We'll need aBreadcrumbs
component to render the list of breadcrumbs and aBreadcrumbsItem
component to render each breadcrumb item.
Start by defining a wrapper component that will list all of the breadcrumbs.
// app/components/Breadcrumbs.tsximport{UIMatch,useMatches}from"@remix-run/react"import{Fragment,HTMLAttributes}from"react"typeBreadcrumbMatch=UIMatch<Record<string,unknown>,{breadcrumb:(data?:unknown)=>JSX.Element}>exportconstBreadcrumbs=({...props}:HTMLAttributes<HTMLElement>)=>{constmatches=(useMatches()asunknownasBreadcrumbMatch[]).filter(({handle})=>handle?.breadcrumb)return(<olitemScopeitemType="https://schema.org/BreadcrumbList"className="flex flex-wrap items-center gap-2.5"{...props}>{matches.map(({handle,data},i)=>(<Fragmentkey={i}><liclassName="contents"itemProp="itemListElement"itemScopeitemType="https://schema.org/ListItem">{i>0&&<spanclassName="text-sm">/</span>}{handle.breadcrumb(data)}<metaitemProp="position"content={`${i+1}`}/></li></Fragment>))}</ol>)}
In the example above, we're using theuseMatches
hook to get the current route matches. We then filter the matches to only include those that have abreadcrumb
handle. We will define this handle in the routes where we want to include a breadcrumb. We then iterate over the matches and render the breadcrumb component for each match.
You may have noticed that we're also passing adata
prop to thehandle.breadcrumb
function. This is because thebreadcrumb
handle can accept data from the route loader. We'll see how to pass data to the breadcrumb handle in the next section.
Next, let's define theBreadcrumbsItem
component that we'll use to render each breadcrumb item.
// app/components/BreadcrumbsItem.tsximport{Link}from"@remix-run/react"import{HTMLAttributes}from"react"exportconstBreadcrumbsItem=({children,...props}:HTMLAttributes<HTMLElement>)=>{return(<Linkto={props.href}itemProp="item"{...props}><spanitemProp="name">{children}</span></Link>)}
Now that we have our components, let's define the breadcrumbs in our routes.
Defining the Breadcrumbs handles in the Routes
In the routes where you want to include breadcrumbs, you need to define abreadcrumb
handle that will render the breadcrumb item.
For example, let's define the following routes:
/
- Home/posts
- Posts/posts/$slug
- Post Details
// app/routes/index.tsximport{BreadcrumbsItem}from"~/components/BreadcrumbsItem"exportconsthandle={breadcrumb:()=><BreadcrumbsItemto="/">Home</BreadcrumbsItem>,}exportdefaultfunctionIndex(){return<h1>Home</h1>}
// app/routes/posts/index.tsximport{BreadcrumbsItem}from"~/components/BreadcrumbsItem"exportconsthandle={breadcrumb:()=><BreadcrumbsItemto="/posts">Posts</BreadcrumbsItem>,}exportdefaultfunctionPosts(){return<h1>Posts</h1>}
// app/routes/posts.$slug.tsximport{json,typeLoaderFunctionArgs}from"@remix-run/node"import{useLoaderData}from"@remix-run/react"import{BreadcrumbsItem}from"~/components/BreadcrumbsItem"exportconsthandle={breadcrumb:(data:{title:string;slug:string})=>(<BreadcrumbsItemto={`/posts/${data.slug}`}>{data.title}</BreadcrumbsItem>),}exportconstloader=({params}:LoaderFunctionArgs)=>{constpost={title:"Example Post",slug:"example-post",}returnjson(post)}exportdefaultfunctionPost(){const{title}=useLoaderData<typeofloader>()return<h1>{title}</h1>}
In the last route, we're using theloader
function to fetch the post data. We then pass this data to thebreadcrumb
handle to render the breadcrumb item. This allows us to dynamically generate the breadcrumb item based on the post data.
Conclusion
Hopefully, this guide has helped you understand how to implement breadcrumbs in your Remix application.
If you're interested to see how breadcrumbs can be implemented in a real-world application, you can check out theOpenAlternative project, or check out thesource code.
If you have any questions or need further clarification, feel free to reach out to me onTwitter.
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse