Dynamic routes recipes from Next.js App Router

Dynamic routes recipes from Next.js App Router

Learn about dynamic routes and other routing essentials from the Next.js App Router in a beginner-friendly way, with many code examples and visuals.


7 min read

Next.js App Router has brought many improvements over its predecessor, Page Router. One of the significant improvements is the routing itself.

Next.js App Router uses the file-system-based router, where you create directories(aka folders) to define routes. You must create a special file called page.js(or .ts/.jsx/.tsx) under the directory to create a page for the route.

The top-level app/ directory acts as the root route(/), and any directory you create under the app directory will create other route segments that map to a URL segment. You can also create the nested route by creating the subdirectories as shown in the image below.

Next.js Routing

In Next.js App Router, a page is unique to a route. You can create one page per route by creating a page.js file under a directory that signifies a route segment.

// /app/page.js
export default function Page() {
  return <h1>Home Page</h1>

If a directory doesn't have a corresponding page.js file, the respective route will be inaccessible and result in a 404, page not found error. The image below demonstrates that each route directory has a page file that creates the page for that route segment.

Next.JS Page Routes

So far, all the routes we have discussed are static and pre-defined, known beforehand to developers to construct the route segments. But what if we do not know the value to form a route segment? What if it is dynamic, like an id, name, or slug?

Welcome Dynamic Routes

You create dynamic routes when you do not know the route segment name and want to create with dynamic data. For example, you may have a static route called /blog. Creating individual blog post routes under it will be too much overhead. You may want to create them using any dynamic data like id, name. or a slug.

๐Ÿ’ก What is a slug?

Slug is an unique part of an URL that provides information about a web page. You should keep them simple, human-readable to help out with SEO. It appears at the end part of the URL after the backslash (โ€œ/โ€). For example, how-to-learn is the slug of the URL https://tapascript.io/blog/how-to-learn.

dynamic routing

Let's Understand Things With Code

We understand things better with code. Let's do that. All the code used in this article is available on the tapaScript GitHub repository.

First, we will create a project with the latest Next.js version. Open a terminal/command prompt and execute the following command. Make sure you have installed Node.js 18 or above.

npx create-next-app@latest

Please provide the details asked. A few important things to note here.

  • Opt for App Router

  • The selection of TypeScript is your choice. If you are comfortable with it, you can select Yes. I'll use JavaScript code in this article as most developers will likely use it(however, I am a big TypeScript fan!).

  • You may opt for the src/ directory. I do that as I like to keep my project configuration files under the src/ directory but outside of the app/ directory. Here, we will assume to opt for it.


Once done, change the directory to your project directory and install the dependencies using yarn/npm/pnpm/bun, whatever you want. Then, start the server locally using the yarn dev or its npm/pnpm/bun equivalent commands.

> cd teach-next-js
> yarn
> yarn dev # or npm run dev or pnpm dev or bun dev

The app will be available on the localhost:3000 by default. As you see in the image below, the app/ directory under the src/ directory signifies the top-level route /. The page.js file under the app/ directory provides us with the page for that route.

folder to route

Let's clear the existing code in the page.js file and put a simple ReactJS component like this:

export default function Home() {
  return (
    <div className="flex justify-center p-2">
      <p className="text-3xl">
        Home Page

Now, our home page(on the route segment /) will look like this:

Home Page

Create More Routes

Let's create a /blog route segment. To do that, we will create a blog/ directory under the app/ directory.

Added Blog

Now you will have a /blog route segment. However, it will not be accessible publicly because we have not defined a page for it yet.

404 page

Let's create a page.js file under the blog/ directory with the following content.

const BlogPage = () => {
  return (
      <p className="text-3xl">Blog Page</p>

export default BlogPage;

We will see the blog page rendered successfully. It is a simple page with a paragraph of text.

blog page

Create Nested Routes

Let us now create a couple of nested routes like /about/form and /about/tax. You guessed it right! We will have to create an about/ directory and then two sub-directories under it. Those are form/ and tax/.

Now, to make all three routes accessible, we need to create a page.js file under each of the directories.

About Page

Here is an example page for the /about route segment. You can create something similar for the pages of /about/form and /about/tax.

const AboutPage = () => {
  return (
      <p className="text-3xl">About Page</p>

export default AboutPage;

Here is how each of the pages may appear for each of the routes.

All Pages

Create Dynamic Routes

Now it's time we start understanding dynamic route segments with code. You can create a dynamic route segment by wrapping the directory name in square brackets([]). For example, [name], [slug], [id], etc.

Taking forward the example of a blog and its posts, the individual posts can include a route like this, /blog/[slug]/page.js. Here [slug] is the dynamic route segment.

Create a directory named [slug] under the blog/ directory and a page.js file under the blog/[slug]/.

blog with slug

All dynamic segments are passed as the params prop to the page. We can destructure the dynamic route segment value from the params prop. Let's create the content for the dynamic route page with the following code.

const BlogPost = ({params: {slug}}) => {
    <p className="text-2xl">
      Showing the blog post for the 
      slug <strong>{slug}</strong>

export default BlogPost;

The page has received a params prop, and we can extract the slug value from it. Now, you can do whatever logic you need using the slug value. You can use it to make a network call and get details to render on the page, or you can just render it on the page, as shown below.

blog slugs

Create Dynamic Catch-all Segments

You can broaden the use of the dynamic route segment([slug]) to a catch-all route segment by adding an ellipsis inside the brackets[...slug].

For example /app/store/[...slug]/page.js will match /store/book, /store/design, and also /store/book/novels, /store/design/shoes, /store/design/shoes/boot, and so on.

Create a store/ directory under the app/ directory. Now, create a directory with the name [...name] under the app/store/.


The page.js file under the app/store/[...name] will define a page for the catch-all route segment. This page component is quite similar to the page component we have seen above for the dynamic routes. However, notice that we are extracting name instead of slug here because we have used [...name] as the directory name.

const StorePage = ({params: {name}}) => {
    <p className="text-2xl">
      Showing the store page for the 
      name <strong>{name}</strong>

export default StorePage;

Now, when you try the route localhost:3000/store/a, the params returns {name: ['a']}. We are printing that on the console and browser.

catch all example 1

You can also catch the route localhost:3000/store/a/b with the same, and the params prop value will be {name: ['a', 'b']}.

catch all example 2

Create Optional Dynamic Catch-all Segments

But hang on! How about the route /store? It gives us 404, page not found.

store route

Yes! One way to fix that is by creating a page.js file inside the app/store/ directory. If you don't want that, you can make the catch-all segment as an optional catch-all segment by wrapping the parameter(or directory name) in double square brackets, [[...name]].

optional catch all

Now even the /store route page will be served from the app/store/[[...name]]/page.js. However, in this case, the value of name will be undefined and the params prop value will be {}.

optional catch all 2

That's all. Thanks for reading it. I hope it was insightful. If you liked the tutorial, please post likes and share it in your circles.

One Last Thing...

I am a self-declared Next.js evangelist who loved this framework from its very beginning days. With the inclusion of the App Router, the affection has gone higher ๐Ÿ˜Š.

I have a full-fledged playlist on my YouTube channel(tapaScript) that will teach you to build projects with Next.js, from clarifying fundamental concepts to coding and tasks.

You may want to check it out.

Let's connect. I share knowledge on web development, content creation, Open Source, and careers on these platforms.

Did you find this article valuable?

Support Tapas Adhikary by becoming a sponsor. Any amount is appreciated!