Remix offers a hook called useMatches that allows you to access the current route matches. You can use this in many different ways, but one of the most common is to build breadcrumbs.
1import { useMatches } from "@remix-run/react";23const matches = useMatches();
Each portion of the route has it's own match object that is returned by the useMatches hook.
Example Project Structure
So let's set up an example of how this would work in a simple project structure for blog posts. Take for example this structure inside of the routes folder:
1// app/routes/2├── articles.tsx3│── articles._index.tsx4│── articles.$slug.tsx5├── index.tsx6└── root.tsx
You have the standard root file that loads your layout. Next there is an articles layout file, index page that holds a list of the artcles, followed by the article page that holds the individual article.
Each file that is part of the route will have it's own match object. With this structure, and the url of /articles/my-first-post, you would have three match objects. The root.tsx, articles.tsx, and articles.$slug.tsx.
1type IMatch = {2 id: string;3 pathname: string;4 data: any;5 params: any;6 handle: any;7};89const matches: IMatch = [10 { id, pathname, data, params, handle }, // root.tsx11 { id, pathname, data, params, handle }, // articles.tsx12 { id, pathname, data, params, handle }, // articles.$slug.tsx13];
Building Breadcrumbs
Let's build out a simple breadcrumb component that will take the matches and render them in the header using the expored handle from each child route when active.
Article Post Page
Here is our Article Post Page where we render the full article. We can export a handle object that will hold our function for rendering a single breadcrumb item. The benefit of using a function over just a string is that we can pass in the match object and get access to all of the data we need to render the breadcrumb.
1// routes/articles/$slug.tsx23export handle = {4 breadcrumb: (match: IMatch) => {5 return <span>/ {match.params.slug}</span>;6 },7};89function ArticlePost() {10 return (11 <article>12 <h1>{match.params.slug}</h1>13 </article>14 );15}
Articles Layout
Here is the main layout for the articles list page and single page. We will use this to render the header with the breadcrumbs. We grab all of the matches from the useMatches hook and filter out the matches that have a breadcrumb in their match.
In the header we can loop through the matches and render the breadcrumbs by calling the breadcrumb function from the handle on each child route. For this example we are going to render the slug to show how you can access params from the match object.
We will also export a handle so we can set the base path for the breadcrumbs. Now we can write the start of the breadcrumbs in one place and not on each child.
1// routes/articles.tsx2export const handle = {3 breadcrumb: (match: IMatch) => {4 return <Link to="/articles">Articles</Link>;5 },6};78export default function ArticlesPageRoute() {9 const matches = useMatches();1011 // filter out matches that have a breadcrumb in their match12 // we don't want to render any matches that don't have a breadcrumb function13 const breadcrumbs = matches.filter(14 (match: IMatch) => match.handle?.breadcrumb,15 );1617 return (18 <div>19 <header>20 <nav>21 {breadcrumbs.map((match: IMatch) => match.handle.breadcrumb(match))}22 </nav>23 </header>24 <main>25 <Outlet />26 </main>27 </div>28 );29}
Conclusion
This is a simple example of how to build breadcrumbs with the useMatches hook in Remix. It's a powerful tool that allows you to build complex features or a simple breadcrumb navigation with ease. What else could you use this for?