ReactHustle

How to Implement Nested Dynamic Routes In NextJS

Jasser Mark Arioste

Jasser Mark Arioste

How to Implement Nested Dynamic Routes In NextJS

Hello, hustler! In this tutorial, you'll learn how to implement dynamic routes in NextJS from basic to advanced. This can be done in the pages or app directory.

Tutorial Objectives #

By the end of this tutorial you should be able to:

  1. Understand basic dynamic routes,
  2. Understand deeply nested dynamic route patterns
  3. Understand the catch-all route.

Starting with the Basics #

To create a dynamic route in NextJS, you have to add brackets [] to a file or folder. Suppose you have the add routes for category pages, so you create file: pages/category/[categorySlug].tsx. We are specifying that the categorySlug parameter will be dynamic. We can then access this parameter in the NextJS router as a query, for example, if you go to /category/react, categorySlug will have the value react:

// pages/category/[categorySlug].tsx
import { useRouter } from "next/router";

const CategoryPage = () => {
  const router = useRouter();
  // categorySlug matches the filename [categorySlug].tsx
  const { categorySlug } = router.query;
  // For the path /category/react. categorySlug => react
  console.log(categorySlug);

  return (
    <div>
      <pre>Category Slug: {categorySlug}</pre>
    </div>
  );
};
export default CategoryPage;
1234567891011121314151617

Combining Static with Dynamic Routes #

It's also possible to combine static routes with dynamic routes.  For example, in addition to pages/category/[categorySlug].tsx, you can create the file pages/category/nextjs.tsx and you'll be able to have access to that static route:

// pages/category/nextjs.tsx
const CategoryNextJS = () => {
  return <div>CategoryNextJS</div>;
};
export default CategoryNextJS;
12345

The static routes takes higher priority over the dynamic route, so if you go to the URL /category/nextjs. It will also display the <CategoryNextJS/> component.

NextJS dynamic routing combined with static.

The Special index.tsx File #

The index.tsx or index.jsx file is special since it's like a static route but if you add it to a directory, it will enable the default route for that directory. For example, if you go to /category, you'll see a 404 error. But once you create the file pages/category/index.tsx, You'll now be able to access /category route:

// pages/category/index.tsx
const CategoryIndexPage = () => {
  return <div>Category Index Page</div>;
};
export default CategoryIndexPage;
12345

Output:

NextJS Dynamic Route Index.tsx file

Creating Nested Routes #

Example 1: /category/{category}/subcategory/{subcategory}

What if you want the route: /category/{category}/subcategory/{subcategory}, how do we implement this? What's the difference from before? Now, the /{category} part is a directory and not a file. To implement this we have to do the following:

  1. Create the folder /pages/category/[category]
  2. Rename or Move the file /pages/category/[category].tsx to /pages/category/[category]/index.tsx
  3. Create the folder /subcategory inside /pages/category/[category]
  4. Create the file [subcategory].tsx inside /subcategory folder.

After all these steps, your directory should look like this:

└ pages
  └ category
    └ [category] # dynamic folder
      ├ index.tsx # previously category/[categorySlug].tsx
      └ subcategory # static folder
        └ [subcategory].tsx #dynamic file

We are combining static and dynamic files or folders, and index.tsx to create the nested route. In your /subcategory/[subcategory].tsx file, you can still access all the query parameters from the parent folders as defined in your folder route.

import { useRouter } from "next/router";
// catgory/[category]/subcategory/[subcategory].tsx
const SubcategoryDynamicPage = () => {
  const router = useRouter();
  const { category, subcategory } = router.query;
  return (
    <div>
      <pre>
        Category : {category} <br></br>
        Subcategory {subcategory}
      </pre>
    </div>
  );
};
export default SubcategoryDynamicPage;
123456789101112131415

Try navigating to http://localhost:3000/category/webdevelopment/subcategory/react, and you should see the output below:

NextJS Nested Dynamic route example 1

Example 2: /category/{category}/{subcategory}

In this example, we have a static /category folder, a dynamic /{category} folderand a dynamic  /{subcategory} file. To implement this, the folder structure should be the below:

└ pages
  └ category # static folder
    └ [category] # dynamic folder
      ├ index.tsx # special file that matches /category/[category]
      └ [subcategory].tsx #dynamic file

For the actual implementations, this is for the /category/[category]/index.tsx file:

import { useRouter } from "next/router";
// pages/category/[category]/index.tsx
const CategoryIndexPage = () => {
  const router = useRouter();
  // Navigating to category/webdevelopment will return category = webdevelopment
  const { category } = router.query;
  return (
    <div>
      [Category] Index Page
      <pre>Category: {category}</pre>
    </div>
  );
};
export default CategoryIndexPage;
1234567891011121314

/category/[category]/[subcategory].tsx file:

import { useRouter } from "next/router";
// pages/category/[category]/[subcategory].tsx
const SubCategoryPage = () => {
  const router = useRouter();
  // Navigating to category/webdevelopment/react will return category = webdevelopment and subcategory = react
  // Navigating to /category/webdevelopment/react/anothercategory will result in a 404 error
  const { category, subcategory } = router.query;
  return (
    <div>
      <pre>
        Category: {category} , Subcategory: {subcategory}
      </pre>
    </div>
  );
};
export default SubCategoryPage;
12345678910111213141516

Catch-All Routes #

Another way to implement example #2 is to use catch-all routes. Catch-all routes allow you to match nested routes such as example #2 by using only one file. To implement this, you can extend [category].tsx by prepending three dots ([...category.tsx]) to the parameter. Your file structure will now look like this:

#bash| |false
└ pages
  └ category # static folder
    └ [...category].tsx # dynamic file matches: /category/webdevelopment/react for example

We now have fewer files which is an advantage of using catch-all routes. For the file implementation, the category query parameter now returns an array. For example:

import { useRouter } from "next/router";
// pages/category/[...category].tsx
const CategoryPage = () => {
  const router = useRouter();
  // category is an array of strings
  const { category } = router.query; //["webdevelopment", "react",...]
  return (
    <div>
      <pre>Categories: {JSON.stringify(category, null, 4)}</pre>
    </div>
  );
};
export default CategoryPage;
12345678910111213

Now navigating to /category/webdevelopment/react will produce the following result:

NextJS catch-all route example

Downsides of using catch-all route:

  1. All the parameters are contained within a single variable, so you'll have to parse the array to get the subcategory.
  2. It's more unrestrained than the previous two examples. So the user can navigate to a route that's not intended like /webdevelopment/react/anothersubcategory, and it will not result in a 404 error.

Full Code #

The full code is available at GitHub: jmarioste/next-js-nested-routes-tutorial. Switch to different branches(main, example-2, and example-3-catch-all) to see the different implementations.

Conclusion #

We learned the ways of implementing dynamic routes in NextJS. In summary, You have to know when to use dynamic folders or files, and index.tsx file to match the exact route that you want. 

If you like this tutorial, please leave a like or share this article. For future tutorials like this, please subscribe to our newsletter or follow me on Twitter.

Resources #

Credits: Image by Felix Merler from Pixabay

Share this post!

Related Posts