Next.js Tutorial
- Next.js Introduction
- Next.js Installation
- Project Structure
- Pages & Routing
- Dynamic Routing
- Linking & Navigating
- Data Fetching (SSR)
- Data Fetching (SSG)
- Client-Side Rendering
- API Routes
- Layouts & Components
- Styling
- Image Optimization
- Font Optimization
- Script Optimization
- Middleware
- Environment Variables
- Error Handling
- Authentication
- React Server Components
- Server Actions
- Suspense & Streaming
- Caching & Revalidation
- TypeScript Integration
- Deployment
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!