Logo
ArticlesTutorialsNewsletter
Log InSign up
Learn NextJS

Understanding Next.js Dynamic Routes: The Difference Between [...path] and [[...path]]


Next.js has revolutionized React development with its powerful routing system. Among its most flexible features are dynamic routes, which allow developers to create pages that can match multiple URL patterns. Two particularly powerful patterns are


ByVictory Lucky
Mar 4, 2025
6 min read
Share this post:
Understanding Next.js Dynamic Routes: The Difference Between [...path] and [[...path]]
#NextJS#Beginner
Introduction

Next.js has revolutionized React development with its powerful routing system. Among its most flexible features are dynamic routes, which allow developers to create pages that can match multiple URL patterns. Two particularly powerful patterns are the catch-all route ([...path]) and the optional catch-all route ([[...path]]). While they might look nearly identical with just an extra pair of brackets, their behavior differs significantly, impacting how your application handles URLs and user navigation.

This guide will break down each pattern in detail, providing clear examples and use cases to help both beginners and experienced Next.js developers make informed decisions about which pattern to use in their projects.

The Basics: Understanding Dynamic Routes in Next.js

Before diving into the specific patterns, let's establish a foundation for understanding Next.js routing.

Next.js uses a file-system based router where:

  • Files in the pages directory (or app directory in the newer App Router) automatically become routes

  • Files named with square brackets like [id].js create dynamic routes that can match different values

Dynamic routes are essential for content-driven websites and applications where the exact number and structure of pages isn't known in advance.

[...path]: The Catch-All RouteWhat It Is

The catch-all route, represented by [...path], allows a single page component to handle multiple URL segments at once.

How It Works

When you create a file named [...path].js in your pages directory, it will match any route with one or more segments where the parameter appears.

Examples

If you have a file at pages/blog/[...slug].js:

βœ… /blog/2023           β†’ matches
βœ… /blog/nextjs/routes  β†’ matches
βœ… /blog/2023/01/post   β†’ matches
❌ /blog                β†’ does NOT match
Accessing Parameters

The matched segments are available as an array via the params object:

// For URL: /blog/2023/nextjs/routes
// In pages/blog/[...slug].js:
export function getServerSideProps({ params }) {
  console.log(params.slug) // ['2023', 'nextjs', 'routes']
  // ...
}
Key Characteristic

Required segments: There must be at least one segment in the path for it to match. The base path alone (/blog in our example) will not match.

[[...path]]: The Optional Catch-All RouteWhat It Is

The optional catch-all route, represented by [[...path]], extends the functionality of the regular catch-all route by making the parameters entirely optional.

How It Works

When you create a file named [[...path]].js, it will match the same routes as a catch-all route, but will also match the base path with no additional segments.

Examples

If you have a file at pages/blog/[[...slug]].js:

βœ… /blog                β†’ matches
βœ… /blog/2023           β†’ matches
βœ… /blog/nextjs/routes  β†’ matches
βœ… /blog/2023/01/post   β†’ matches
Accessing Parameters

Similar to the catch-all route, but with a special case for the base path:

javascript

// For URL: /blog
// In pages/blog/[[...slug]].js:
export function getServerSideProps({ params }) {
  console.log(params.slug) // undefined or []
  // ...
}

// For URL: /blog/2023/nextjs
// In pages/blog/[[...slug]].js:
export function getServerSideProps({ params }) {
  console.log(params.slug) // ['2023', 'nextjs']
  // ...
}
Key Characteristic

Optional segments: The parameter can match zero or more segments, making it entirely optional.

Practical Use CasesWhen to Use [...path] (Catch-All)
  1. Documentation sites: When you need to organize content in a hierarchical structure

    /docs/getting-started/installation
    /docs/advanced/optimization/images
    
  2. Blog posts with categories: When posts are organized by date, category, or other hierarchical metadata

    /blog/technology/javascript/es6-features
    
  3. Product categories: For e-commerce sites with nested product categories

    /products/electronics/computers/laptops
    
When to Use [[...path]] (Optional Catch-All)
  1. Content sections with landing pages: When you need both a section landing page and nested content

    /learn                 (Overview page)
    /learn/basics          (Specific content)
    /learn/advanced/topics (Deeper nested content)
    
  2. Search functionality: Where you might have optional filters in the URL

    /search                (Base search)
    /search/products/shoes (Filtered search)
    
  3. User profiles with tabs: Where you have a main profile page and optional sections

    /profile               (Main profile)
    /profile/posts         (Posts tab)
    /profile/friends/list  (Friends list tab)
    
Implementation Tips and Best Practices1. Handle Parameter Validation

Always validate the parameters to ensure they make sense for your application logic:

javascript

export async function getServerSideProps({ params }) {
  // For [[...slug]], check if params.slug exists
  const slugs = params.slug || [];
  
  // Validate the path segments
  if (slugs.length > 3) {
    return {
      notFound: true // Return 404 page for invalid paths
    };
  }
  
  // Continue with valid paths
  return {
    props: {
      // ...
    }
  };
}
2. Route Priority in Next.js

Next.js has a predefined route matching priority:

  1. Static routes (/about)

  2. Dynamic routes (/posts/[id])

  3. Catch-all routes (/posts/[...slug])

  4. Optional catch-all routes (/posts/[[...slug]])

This means more specific routes will take precedence over catch-all routes. Use this to your advantage when designing your routing structure.

3. Combine with Middleware for Advanced Control

Next.js middleware can be combined with catch-all routes for even more control:

javascript

// middleware.js
export function middleware(request) {
  const url = request.nextUrl.clone();
  const { pathname } = url;
  
  // Handle specific paths within a catch-all pattern
  if (pathname.startsWith('/dashboard/')) {
    // Check authentication, redirect, etc.
  }
}

export const config = {
  matcher: ['/dashboard/:path*'],
};
Common Gotchas and Solutions1. Data Fetching with Dynamic Segments

When fetching data based on dynamic segments, remember to handle all possible cases:

javascript

async function fetchData(slugs = []) {
  if (slugs.length === 0) {
    return fetchOverviewData();
  } else if (slugs.length === 1) {
    return fetchCategoryData(slugs[0]);
  } else {
    return fetchNestedData(slugs);
  }
}
2. TypeScript Type Safety

For TypeScript users, properly type your route parameters:

typescript

// For [...slug].js
type Params = {
  slug: string[];
};

// For [[...slug]].js
type Params = {
  slug?: string[];
};

export const getServerSideProps: GetServerSideProps<Props, Params> = 
  async ({ params }) => {
    const slugs = params?.slug || [];
    // ...
  };
3. Link Component Usage

When using Next.js's Link component with dynamic routes, ensure you're passing the correct href structure:

// For nested paths
<Link href="/blog/[...slug]" as={`/blog/${category}/${year}/${post}`}>
  Read post
</Link>

// For optional catch-all routes
<Link href="/profile/[[...tab]]" as={tab ? `/profile/${tab}` : "/profile"}>
  View profile
</Link>
Migrating Between Route Types

If you need to migrate from a catch-all route to an optional catch-all route (or vice versa), consider these steps:

  1. Create the new route file with the updated pattern

  2. Update your component to handle the difference in parameter behavior

  3. Add redirects for any changed URL patterns in your next.config.js:

module.exports = {
  async redirects() {
    return [
      {
        source: '/blog',
        destination: '/blog/all',
        permanent: true,
      },
    ];
  },
};
Performance Considerations1. Static Generation vs. Server-Side Rendering

When using catch-all routes with getStaticProps, you'll need to define which paths to pre-render using getStaticPaths:

javascript

export async function getStaticPaths() {
  // For [...slug] (regular catch-all)
  return {
    paths: [
      { params: { slug: ['2023'] } },
      { params: { slug: ['2023', 'nextjs'] } },
    ],
    fallback: 'blocking'
  };
  
  // For [[...slug]] (optional catch-all)
  // Don't forget to include the base path
  return {
    paths: [
      { params: { slug: [] } }, // Important for [[...slug]]
      { params: { slug: ['2023'] } },
      { params: { slug: ['2023', 'nextjs'] } },
    ],
    fallback: 'blocking'
  };
}
2. Incremental Static Regeneration

For data-driven sites, combine catch-all routes with Incremental Static Regeneration for optimal performance:

export async function getStaticProps({ params }) {
  const slugs = params.slug || [];
  const data = await fetchData(slugs);
  
  return {
    props: {
      data,
    },
    revalidate: 60, // Regenerate page after 60 seconds
  };
}
Conclusion

Understanding the difference between [...path] and [[...path]] in Next.js is crucial for building flexible, intuitive routing structures in your applications. The standard catch-all route ([...path]) is perfect for hierarchical content where you need at least one parameter, while the optional catch-all route ([[...path]]) provides additional flexibility by making the parameter entirely optional.

By choosing the right pattern for your specific use case, you can create more intuitive URLs, simplify your code, and improve the overall user experience of your Next.js application. Remember that routing is a fundamental aspect of web application usability, and thoughtful implementation will pay dividends in maintainability and user satisfaction.

Additional Resources
  • Next.js Documentation on Dynamic Routes

  • Next.js GitHub Repository

  • Vercel Platform - The creators of Next.js

If you found this tutorial helpful, feel free to share it with others. And don’t forget to follow me on Twitter/X for more web development tips and tutorials.
Happy coding! πŸš€


---

Connect with Me
- GitHub: https://github.com/lucky-victory
- Twitter: @CodewithVick

Subscribe to our newsletter

Get the latest posts delivered right to your inbox!