Dynamic Routing in Next.js
Creating dynamic routes with URL parameters
🔀 What is Dynamic Routing?
Dynamic routing allows you to create pages with variable URL segments. Use square brackets in folder names to capture URL parameters and display different content based on the URL.
// app/blog/[slug]/page.js
export default function Post({ params }) {
return <h1>Post: {params.slug}</h1>
}
Dynamic Route Types
Single Parameter
One dynamic segment
Multiple Parameters
Nested dynamic segments
Optional Parameters
Optional catch segments
Catch-All Routes
Multiple segments
🔹 Single Dynamic Segment
Create a dynamic route by wrapping a folder name in square brackets. The parameter name becomes available in your page component through the params prop.
🔸 Folder Structure
app/
└── blog/
└── [slug]/
└── page.js → /blog/hello-world
→ /blog/nextjs-tutorial
🔸 Accessing Parameters
// app/blog/[slug]/page.js
export default function BlogPost({ params }) {
return (
<div>
<h1>Blog Post</h1>
<p>Reading: {params.slug}</p>
</div>
)
}
// URL: /blog/hello-world
// params.slug = "hello-world"
🔸 Fetching Data with Parameters
// app/blog/[slug]/page.js
async function getPost(slug) {
const res = await fetch(`https://api.example.com/posts/${slug}`)
return res.json()
}
export default async function BlogPost({ params }) {
const post = await getPost(params.slug)
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
)
}
🔹 Multiple Dynamic Segments
Nest multiple dynamic segments to create complex URL structures. Each segment becomes a separate parameter accessible in your component.
🔸 Folder Structure
app/
└── shop/
└── [category]/
└── [product]/
└── page.js → /shop/electronics/laptop
→ /shop/clothing/shirt
🔸 Using Multiple Parameters
// app/shop/[category]/[product]/page.js
export default function Product({ params }) {
return (
<div>
<h1>Product Page</h1>
<p>Category: {params.category}</p>
<p>Product: {params.product}</p>
</div>
)
}
// URL: /shop/electronics/laptop
// params.category = "electronics"
// params.product = "laptop"
🔹 Generating Static Paths
For static site generation, use generateStaticParams to pre-render dynamic routes at build time. This improves performance by creating pages ahead of time.
🔸 Generate Static Params
// app/blog/[slug]/page.js
export async function generateStaticParams() {
const posts = await fetch('https://api.example.com/posts')
.then(res => res.json())
return posts.map((post) => ({
slug: post.slug,
}))
}
export default async function BlogPost({ params }) {
const post = await getPost(params.slug)
return <h1>{post.title}</h1>
}
// Generates pages at build time:
// /blog/first-post
// /blog/second-post
// /blog/third-post
🔹 Generating Metadata
Create dynamic page titles and meta tags based on URL parameters. This is essential for SEO and social media sharing.
🔸 Dynamic Metadata
// app/blog/[slug]/page.js
export async function generateMetadata({ params }) {
const post = await getPost(params.slug)
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
images: [post.image],
},
}
}
export default async function BlogPost({ params }) {
const post = await getPost(params.slug)
return <article>{post.content}</article>
}
🔹 Linking to Dynamic Routes
Use the Link component to navigate to dynamic routes. Pass the parameter values directly in the href prop for clean, readable code.
🔸 Static Links
import Link from 'next/link'
export default function BlogList() {
return (
<div>
<Link href="/blog/hello-world">
Hello World Post
</Link>
<Link href="/blog/nextjs-guide">
Next.js Guide
</Link>
</div>
)
}
🔸 Dynamic Links
import Link from 'next/link'
export default function BlogList({ posts }) {
return (
<div>
{posts.map(post => (
<Link key={post.id} href={`/blog/${post.slug}`}>
{post.title}
</Link>
))}
</div>
)
}
🔹 Handling Not Found
Handle invalid dynamic parameters gracefully by returning a 404 page when content doesn't exist. Use notFound() function for proper error handling.
🔸 Not Found Handling
import { notFound } from 'next/navigation'
async function getPost(slug) {
const res = await fetch(`https://api.example.com/posts/${slug}`)
if (!res.ok) return null
return res.json()
}
export default async function BlogPost({ params }) {
const post = await getPost(params.slug)
if (!post) {
notFound()
}
return <h1>{post.title}</h1>
}
🔹 Best Practices
Follow these guidelines when working with dynamic routes:
- Use descriptive names: [slug], [id], [username] instead of [param]
- Validate parameters: Check if data exists before rendering
- Handle errors: Use notFound() for missing content
- Generate static paths: Pre-render pages when possible
- Add metadata: Dynamic titles and descriptions for SEO
- Type safety: Use TypeScript for parameter types