Building Dynamic Breadcrumbs in Next.js App Router
Originally Published on 2024-04-29
Updated on 2024-05-24
I've been wanting to figure out how to access the route parameters in a Next.js App Router project for a while and I recently came by a tweet from @fredkisss pointing to a Pull Request that had just landed in Next.js.The Pull Request he linked is support breadcrumb style catch-all parallel routes by ztanner, a Vercel employee and Next.js maintainer.
The Pull Request added functionality and demonstrates how to use the Parallel Routes feature to access the route parameters. I would encourage you to read through that documentation to get a better understanding of what Parallel Routes are but I will also summarize concepts as I write this out.
Parallel Route Slots#
To start on building our breadcrumbs we have to understand what slots are in Parallel Routes. slots are defined as
Parallel routes are created using named slots. Slots are defined with the @folder convention. ... Slots are passed as props to the shared parent layout.
What this means is that we can add a folder to our project named app/@breadcrumbs
to create a slot. To be able to
build out our breadcrumbs, we'll want to use a catch-all segment
such as app/@breadcrumbs/[...catchAll]
. Finally, we'll add a page which will render the breadcrumbs
app/@breadcrumbs/[...catchAll]/page.tsx
. For now, let's add a little placeholder until we're ready to build our
breadcrumbs.
The logging statement will print out the array of route parameters that have been found.
default.tsx
A note when using Parallel Routes is that a default file is required. The docs say
On refresh, Next.js will render a default.js for @analytics. If default.js doesn't exist, a 404 is rendered instead.
We don't want to render a 404 page so we need to add app/@breadcrumbs/default.tsx
. In this case we're going to render
an empty fragment to satisfy the requirements.
layout.tsx
Rendering the slot is covered more extensively in the Parallel Routes documentation linked above but a way to think
about it is that children
is a special slot that is automatically provided. To add our defined slots, we can mimic
the way children
are passed into the layout. We'll change our root layout at app/layout.tsx
.
Nested Dynamic Routes#
The Pull Request I referenced at the start added the functionality for deeply nested dynamic routes to work with
Parallel Routes so let's add in some routes to test this feature out. Let's go with names and add
app/[first]/[middle]/[last]/page.tsx
. We don't need to add anything to this page so we'll return a simple message
to indicate where we're at.
With this route in place, we can visit http://localhost:3000/Joseph/Francis/Tribbiani
and we'll see
rendering in @breadcrumbs [ 'Joseph', 'Francis', 'Tribbiani' ]
logged from the server. This is great and we'll
be able to build our breadcrumbs for this page.
Static + Dynamic Routes#
Since the original publish date of this article, a new Pull Request, Provide non-dynamic segments to catch-all parallel routes, has been added to Next.js which makes this functionality in this section possible.
I wanted to add this section because my personal site, which you're reading this post on, does not have a set of
deeply nested dynamic routes, instead I have a blog/
route with a single dynamic route [slug]
underneath it.
To demonstrate this, we can add app/blog/[slug]/page.tsx
.
If we visit http://localhost:3000/blog/new-blog-post
we'll see rendering in @breadcrumbs [ 'blog', 'new-blog-post' ]
.
Next.js is properly adding the static blog
path into our params so we're able to build out our breadcrumbs for the entire
site with a single parallel route!
shadcn/ui Breadcrumb#
I use shadcn/ui for the components on this website and will be laying out how to use the
route parameters to build the breadcrumbs using components from it. However, the same principles should apply to
components from any library. We'll be using the Breadcrumb to build
out our own Breadcrumbs
component. Given that we have an array of nested routes (from our logged output above) we'll
need to construct longer and longer href
attributes to pass into the BreadcrumbLink
component. I decided to use a
regular old for
loop because I've been doing a bunch of data structures practice but if you want to use
routes.forEach
, go for it; either way, the concept is the same. We will build out clickable links for every part of the
route except for the final one, which will be just a static representation of the current page as a BreadcrumbPage
.
Now that we have our Breadcrumbs
component we can update our slot to render it.
Conclusion#
The final file structure of the application looks like this
I hope you were able to learn a little bit more about how the App Router works with Parallel Routes and how slots can be used to render different data depending on the route. I think this is a really powerful concept that I'll be exploring more in the future.