Page Router
Mastering Dynamic Routing in Next.js: A Comprehensive Guide
Next.js, a powerful React framework, has revolutionized modern web development with features like server-side rendering (SSR), static site generation (SSG), and incremental static regeneration (ISR). Among its most powerful capabilities is dynamic routing, which allows developers to create flexible, parameterized URLs for scenarios like blogs, e-commerce product pages, user profiles, and more. In this guide, we’ll dive deep into Next.js dynamic routing, covering everything from basic setups to advanced patterns.
1. What is Dynamic Routing?
Dynamic routing enables URLs to adapt based on parameters, making it ideal for pages that render content dynamically. For example:
/posts/1→ Displays a blog post with ID1./products/nextjs-handbook→ Shows details for a product named "nextjs-handbook".
Next.js implements dynamic routing using file-system-based routing with special syntax like square brackets ([param]).
2. Basic Dynamic Routing Setup
Step 1: Create a Dynamic Route
Inside the pages directory, create a file with square brackets to denote a dynamic segment:
pages/
posts/
[id].js ← Handles paths like /posts/1, /posts/abc, etc.
Step 2: Access Route Parameters
Use Next.js’s useRouter hook or data-fetching methods (getStaticProps, getServerSideProps) to retrieve parameters:
// pages/posts/[id].js
import { useRouter } from 'next/router';
export default function Post() {
const router = useRouter();
const { id } = router.query; // Access the `id` parameter from the URL
return <h1>Post ID: {id}</h1>;
}
3. Pre-Rendering Dynamic Pages
Next.js supports pre-rendering dynamic pages at build time (SSG) or runtime (SSR). Here’s how to implement both strategies:
Case 1: Static Generation (SSG)
Generate pages at build time using getStaticPaths and getStaticProps:
// pages/posts/[id].js
export async function getStaticPaths() {
// Fetch all possible IDs (e.g., from an API or database)
const paths = [
{ params: { id: '1' } },
{ params: { id: '2' } },
];
return {
paths,
fallback: false, // Other routes → 404
};
}
export async function getStaticProps({ params }) {
// Fetch data for the specific post using `params.id`
const postData = await fetchPostById(params.id);
return {
props: { postData }, // Pass data to the page component
};
}
function Post({ postData }) {
return (
<article>
<h1>{postData.title}</h1>
<p>{postData.content}</p>
</article>
);
}
Key Options for fallback:
fallback: false→ 404 for unknown paths.fallback: true→ Render a fallback UI while generating the page on-demand.fallback: 'blocking'→ Server-render unknown paths on first request.
Case 2: Server-Side Rendering (SSR)
Fetch data on every request using getServerSideProps:
export async function getServerSideProps({ params }) {
const postData = await fetchPostById(params.id);
return { props: { postData } };
}
4. Advanced Dynamic Routing Patterns
Nested Dynamic Routes
Create multi-segment dynamic routes using nested folders:
pages/
blog/
[year]/
[month]/
[slug].js ← Matches /blog/2023/08/nextjs-tips
Access parameters in the component:
const router = useRouter();
const { year, month, slug } = router.query; // { year: '2023', month: '08', slug: 'nextjs-tips' }
Catch-All Routes
Capture all subsequent path segments using [...slug].js:
pages/
docs/
[...slug].js ← Matches /docs/a, /docs/a/b, /docs/a/b/c...
Parameters are passed as an array:
const { slug } = router.query; // slug = ['a', 'b', 'c']
Optional Catch-All Routes
Use [[...slug]].js to make segments optional:
pages/
categories/
[[...slug]].js ← Matches /categories, /categories/a, /categories/a/b...
5. Practical Example: Building a Blog System
Let’s build a blog with the following routes:
- Post list:
/posts - Post details:
/posts/[id] - Category filter:
/categories/[category]
Step 1: Define Routes
pages/
posts/
index.js ← Post list page
[id].js ← Post details
categories/
[category].js ← Filtered posts by category
Step 2: Implement Dynamic Post Details
// pages/posts/[id].js
export async function getStaticPaths() {
const allPostIds = await fetchAllPostIds(); // Fetch IDs from an API
const paths = allPostIds.map(id => ({ params: { id } }));
return { paths, fallback: 'blocking' }; // Generate missing pages on-demand
}
export async function getStaticProps({ params }) {
const postData = await fetchPostData(params.id);
return { props: { postData } };
}
6. Best Practices & Common Pitfalls
Handling Loading States
When using fallback: true, display a loading state while the page is generated:
const router = useRouter();
if (router.isFallback) {
return <div>Loading...</div>;
}
SEO Optimization
- Use
getStaticPropsfor SSG to pre-render SEO-friendly content. - Add
titleandmetatags dynamically withnext/head.
Avoiding Parameter Conflicts
Ensure dynamic route filenames don’t clash with static routes. For example, pages/posts/[id].js and pages/posts/recent.js will conflict.
7. Conclusion
Next.js dynamic routing is a game-changer for building scalable, SEO-friendly applications. By combining it with SSG or SSR, you can deliver blazing-fast performance while handling complex URL structures. Whether you’re building a blog, e-commerce site, or dashboard, dynamic routing ensures your app remains flexible and maintainable.
Next Steps:
- Explore Next.js’s API Routes for full-stack capabilities.
- Learn about shallow routing for updating URLs without reloading the page.
Happy coding! 🚀
Further Reading: