Creating Multiple Dynamic Routes with NextJs

Creating Multiple Dynamic Routes with NextJs

If you’ve used NextJs then you will probably be familiar with how it handles routing using the file system.

Files named page.tsx are organized into folders and the name of the folder becomes the name of the route. E.g. If the folder were named about-us then the route on the site will be /about-us. To create a page under another page, simply create a folder within a folder. E.g. company/about-us/page.tsx becomes /company/about-us.

NextJs Routing Structure

Page names can also be dynamic, to do this you use a catch-all route which is defined with square brackets in the folder name. e.g. [slug]/page.tsx.

NextJs Routing Structure with dynamic route

Now all requests to the route of the site which don’t match a fixed path will be picked up by this page and will include a parameter matching the name within the square backets (in this example “slug”), which you can then use to render the page dynamically.

1export default function Page({ params }: { params: { slug: string } }) {
2 return <div>My Page Name: {params.slug}</div>
3}

Dynamic pages can also be nested. E.g. [category]/[product]/page.tsx. In this example a request to /electronics/50-inch-tv will route to the page including the parameters category=electronics, and product=50-inch-tv.

By using triple dots in-front of the parameter name NextJs will catch all requests to subpages and provide the hierarchy as an array to the parameter. E.g. […slug]/page.tsx will capture requests to /company/about-us and have a parameter named slug with the values [‘company’, ‘about-us’].

This is all explained on the NextJs site in detail https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes#catch-all-segments

However, the one thing you can’t do is have two or more dynamic paths at the same level. E.g. Creating…

/[category]/page.tsx

/[slug]/page.tsx

Will lead to an error saying "Error: You cannot use different slug names for the same dynamic path".

The official way to have more than one dynamic path is to separate them by placing one into a subfolder, so in my scenario I could have…

/products/[category]/page.tsx

/[slug]/page.tsx

Now any routes starting /products/ will be routed to the category page rather than the slug page. However this typically doesn’t match the requirement and hasn’t been needed for the majority of sites being built with a CMS.

Creating multiple dynamic paths in NextJs using one catch all page

The first option for enabling multiple dynamic paths at the same level is to have one catch-all routes and within that file figure out what the page should be. E.g. /[…slug]/page.tsx

Now all requests to the site will be captured by this route and within the page you can write custom logic to determine if the first value is a category or generic page.

To keep code clean the actual rendering of the different page types can be split into separate components with page.tsx handling the logic for which to include.

This setup works well, however it has a tradeoff that a lot of functionality provided by NextJs through it’s folder structure is lost. E.g. NextJs support layout files at different levels of the file system but here you would be restricted to just the one.

Creating multiple dynamic paths in NextJs using rewrites

An alternative method is to structure the dynamic pages into separate subfolders as per the official way and then hide this using Rewrites.

What is a Rewrite?

A rewrite is like a redirect, but one that happens server side and is therefore invisible to the user. When a request goes into the server it is first checked against the rewrite rules and if it matches one, the request is rewritten to that URL rather than being passed as it to the application.

Anyone old enough to remember programming in Classic ASP may remember this being one of the methods first used to implement friendly URLs rather than having query strings for everything dynamic.

How to setup a rewrite in NextJs

Rewrites are configured in the next.config.mjs file.

1module.exports = {
2 async rewrites() {
3 return {
4 beforeFiles: [
5 {
6 source: '/electronics/:slug*',
7 destination: '/products/electronics/:slug*',
8 },
9 ]
10 },
11 },
12}

In this example I am creating a re-write for anything going to a path starting /electronics/ to route the request to the path /products/electronics/

Hardcoding the list of categories as re-writes isn’t much better than creating a lot of hard coded sub-folders, but this is easily fixed by dynamically writing the list which will happen when the site is built.

Creating a redirect in NextJs

The solution now works, however any requests to the /products/ url will also resolve as duplicate pages. To overcomes this we can create a standard redirect to put people back on our preferred path.

1module.exports = {
2 async redirects() {
3 return [
4 {
5 source: '/products/electronics/:slug*',
6 destination: '/electronics/:slug*',
7 permanent: true,
8 },
9 ]
10 },
11}

One thing to be aware of

While this is a solution that will work, one thing to note is that there may be a limit to how far it will scale. If you hit the point of creating over 1000 redirects or rewrites the NextJs build script will start warning of potential performance hits from there being to many redirects.

This isn’t something I’ve experienced, but each rewrite is effectively another check that needs to be performed against each request.