Dynamic Routing

It is rare that a modern web application relies entirely on fixed paths like /about or /contact. Instead, you often need to render dynamic content based on external data, like a blog post slug (/blog/first-post) or an e-commerce product ID (/products/9921).

Next.js allows you to create Dynamic Routes easily by adding square brackets [param] to a folder or file name.

Dynamic Routing in App Router (`app/`)

To create a dynamic route in the App Router, enclose the folder's name in brackets, such as [id] or [slug]. Then, add a page.js file inside it.

app/
└── blog/
    ├── page.js           --> Route: /blog
    └── [slug]/
        └── page.js       --> Route: /blog/:slug (e.g. /blog/hello-world)

Accessing the Dynamic Parameter

Next.js passes the dynamic segments to the page component via a params prop. In Server Components (App Router default), you access it asynchronously or synchronously depending on the version.

// app/blog/[slug]/page.js
export default function BlogPost({ params }) {
  // Access the dynamic 'slug' parameter
  const { slug } = params;

  return <h1>Reading Post: {slug}</h1>;
}

If you visit /blog/my-first-post, it will render "Reading Post: my-first-post".

Dynamic Routing in Pages Router (`pages/`)

In the classic Pages router, you use square brackets in the filename itself.

pages/
└── products/
    ├── index.js          --> Route: /products
    └── [id].js           --> Route: /products/:id (e.g. /products/123)

You can extract the ID using the Next.js `useRouter` hook on the client-side, or via Context/Query in server-side functions (like `getServerSideProps`).

// pages/products/[id].js
import { useRouter } from 'next/router';

export default function ProductDetail() {
  const router = useRouter();
  const { id } = router.query;

  return <h1>Product Details for Item #{id}</h1>;
}

Catch-All Segments

What if you want to capture multiple segments, such as /shop/clothes/shirts/red? You can extend dynamic routing to "catch all" segments by adding an ellipsis [...param].

app/
└── shop/
    └── [...slug]/
        └── page.js

Visiting /shop/clothes/shirts/red would provide the `params.slug` as an array: ['clothes', 'shirts', 'red'].

Optional Catch-All Segments

By surrounding the ellipsis in double brackets [[...slug]], the route will also match the root of the catch-all. In the example above, visiting simply /shop would also map to this file (without causing a 404).

Conclusion

Dynamic routing is perfect for blog engines, SaaS dashboards, and e-commerce websites where pages are driven directly by a database schema. Now that you know how to build dynamic routes, we need to know how to navigate users between them!