[Next.js Tutorial] Dynamic Routes: How to Use and Optimize

In modern web development, creating pages with flexible, data-driven content is essential. You can’t manually create thousands of pages for products, blog posts, or user profiles. This is where Dynamic Routes in Next.js shine, offering a powerful, flexible, and SEO-optimized solution.

Dynamic Routes in Nextjs

This article will take you from the basics to advanced techniques, so you can fully master Dynamic Routes in your Next.js projects.

What are Dynamic Routes? Why are they important?

Imagine you’re building an e-commerce site. Instead of creating a separate file for each product (product-1.js, product-2.js, ...), you just create a single "template" page, and its content changes automatically based on the product’s ID or a URL-friendly "slug".

That’s the power of Dynamic Routes.

Dynamic Routes Example

Dynamic Routes are routes (URLs) that aren’t hardcoded, but are generated flexibly based on data—such as product IDs, blog post titles, or usernames.

Key benefits:

  • Component reuse: One page layout for countless subpages with similar structure.
  • Easy management: No need to handle hundreds or thousands of separate files.
  • SEO-friendly URLs: Create meaningful, readable URLs (e.g., /blog/how-to-use-nextjs instead of /blog?id=123), improving search rankings.
  • Flexible with data: Easily display content from any data source (API, database, markdown files, ...).

Syntax and How It Works in the App Router

With the App Router, defining Dynamic Routes is more intuitive than ever, based on folder structure.

1. Basic Dynamic Segments

To create a dynamic route, just name a folder in square brackets: [folderName]. The name inside the brackets becomes a parameter for your route.

Example: Create a blog post detail page.

Your folder structure:

app/
└── blog/
    └── [slug]/
        └── page.js
  • With this structure, any URL like /blog/post-a, /blog/what-is-nextjs, or /blog/123 will be handled by app/blog/[slug]/page.js.
  • The values "post-a", "what-is-nextjs", or "123" are the slug parameter.

How to get the parameter value?

In page.js (a default Server Component), Next.js automatically passes route parameters into the component’s props.

// app/blog/[slug]/page.js

// Example data fetch function
async function getPostData(slug) {
  // Suppose you fetch data from an API
  const res = await fetch(`https://api.example.com/posts/${slug}`)
  if (!res.ok) return { title: 'Post not found', content: '' }
  return res.json()
}

export default async function BlogPostPage({ params }) {
  // params is an object: { slug: 'post-a' }
  const { slug } = params
  const post = await getPostData(slug)

  return (
    <div>
      <h1>{post.title}</h1>
      <p>
        This is the content for the post with slug: <strong>{slug}</strong>
      </p>
      <div dangerouslySetInnerHTML={{ __html: post.content }} />
    </div>
  )
}

2. Catch-all Segments

Want a route to "catch" multiple URL segments? For example, a docs page with URLs like /docs/getting-started/installation.

Use the spread syntax inside brackets: [...folderName].

Example:

Folder structure:

app/
└── docs/
    └── [...slug]/
        └── page.js
  • This route matches /docs/a, /docs/a/b, /docs/a/b/c, and so on.
  • The slug parameter is now an array of segments.

How to get the value:

// app/docs/[...slug]/page.js

export default function DocsPage({ params }) {
  // For /docs/getting-started/installation
  // params: { slug: ['getting-started', 'installation'] }
  const { slug } = params

  return (
    <div>
      <h1>Docs for: {slug.join(' / ')}</h1>
      {/* Render content based on the slug array */}
    </div>
  )
}

3. Optional Catch-all Segments

A variant of Catch-all is Optional Catch-all, which lets the route match the base URL (no parameter). Use double brackets: [[...folderName]].

Example:

Folder structure:

app/
└── shop/
    └── [[...filters]]/
        └── page.js
  • This route matches /shop (no parameter).
  • It also matches /shop/laptops, /shop/laptops/apple, ...

How to get the value:

// app/shop/[[...filters]]/page.js

export default function ShopPage({ params }) {
  // For /shop, params: {}
  // For /shop/laptops/apple, params: { filters: ['laptops', 'apple'] }
  const { filters } = params

  return (
    <div>
      {filters ? (
        <h1>Filtering products by: {filters.join(' > ')}</h1>
      ) : (
        <h1>Showing all products</h1>
      )}
    </div>
  )
}

Performance Optimization with generateStaticParams

One of Next.js’s most powerful features is Static Site Generation (SSG). With Dynamic Routes, you can pre-generate static pages at build time for all possible parameters. This makes your site lightning fast and reduces server load.

The generateStaticParams function lets you do this.

How it works:

  1. In the dynamic route’s page.js, export an async function called generateStaticParams.
  2. This function returns an array of objects, each representing the params for a static page to generate.

Example: Pre-generate all blog post pages.

// app/blog/[slug]/page.js

// 1. Fetch all post slugs
async function getAllPostSlugs() {
  const res = await fetch('https://api.example.com/posts')
  const posts = await res.json()
  // Return in the format generateStaticParams expects
  return posts.map((post) => ({
    slug: post.slug,
  }))
}

// 2. Export generateStaticParams
export async function generateStaticParams() {
  const slugs = await getAllPostSlugs()
  return slugs
}

// The data fetch function and Page component remain as in the first example
async function getPostData(slug) {
  /* ... */
}

export default async function BlogPostPage({ params }) {
  /* ... */
}

When you run npm run build, Next.js will:

  1. Call generateStaticParams.
  2. Get a list like [{ slug: 'post-a' }, { slug: 'post-b' }].
  3. Render /blog/post-a and /blog/post-b as static HTML files.

When users visit these URLs, they get the HTML instantly—no server render wait, for a blazing fast experience.

Note: If a user visits a slug not generated by generateStaticParams, Next.js will render that page on-demand, just like a normal Server Component.

Conclusion: The Power is in Your Hands

Dynamic Routes in Next.js are not just a technical feature—they’re a foundational tool for building complex, data-rich, scalable web apps. By combining intuitive folder structure with the power of Server Components and static page generation via generateStaticParams, you can deliver fast, smooth, and SEO-optimized user experiences.

Hopefully, this article has given you a deep and comprehensive understanding of Dynamic Routes. Start applying them in your next Next.js project and unlock their endless possibilities!

Related Posts

[Next.js Tutorial] Navigation and Linking: How to Use for Optimal Performance

This article will help you master Navigation and Linking in Next.js with ease. Learn in detail how to use Next/link and useRouter for page navigation and optimize your app’s performance.

[Next.js Tutorial] API Routes: Build Your Backend Quickly and Efficiently

Learn how to build a powerful backend right inside your Next.js project with API Routes. This guide covers the basics, how to create API endpoints, handle requests and responses, and real-world deployment. Save time and optimize performance!

[Next.js Tutorial] Basic Directory Structure and Routing in Your Project

How to organize your Next.js project effectively? A detailed guide on directory structure and basic routing, helping you build Next.js websites quickly and properly.

[Next.js Tutorial] Data Fetching in Server Components: How to Optimize Effectively

A deep dive into how Data Fetching works with Server Components in Next.js. Discover best practices for querying data, optimizing performance, and building faster, smoother apps.