How to Implement Nested Dynamic Routes In NextJS
Jasser Mark Arioste
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:
- Understand basic dynamic routes,
- Understand deeply nested dynamic route patterns
- 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.
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:
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:
- Create the folder
/pages/category/[category]
- Rename or Move the file
/pages/category/[category].tsx
to/pages/category/[category]/index.tsx
- Create the folder
/subcategory
inside/pages/category/[category]
- 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:
Example 2: /category/{category}/{subcategory}
In this example, we have a static /category
folder, a dynamic /{category}
folder, and 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:
Downsides of using catch-all route:
- All the parameters are contained within a single variable, so you'll have to parse the array to get the subcategory.
- 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