LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 1412 min read

Catch-all Segments

Match multiple path segments at once using [...slug] and optional catch-all [[...slug]] routes.

Catch-all Segments

Adding three dots inside brackets, like [...slug], creates a catch-all segment that matches any number of path parts and collects them into an array.

app/docs/[...slug]/page.tsx
/docs/a → params.slug === ["a"]
/docs/a/b → params.slug === ["a", "b"]
/docs/a/b/c → params.slug === ["a", "b", "c"]
/docs → does NOT match — needs at least one segment

Optional Catch-all Segments

Wrapping the folder in double brackets, [[...slug]], makes the segment optional — it also matches the parent path with zero extra segments.

app/docs/[[...slug]]/page.tsx
/docs → params.slug === undefined
/docs/a → params.slug === ["a"]
/docs/a/b → params.slug === ["a", "b"]

Real-World Use Case

Catch-all segments are the natural fit for documentation sites and CMS-driven pages, where content lives at arbitrarily nested paths (/docs/guides/routing/dynamic) that you don't want to hand-create a folder for.

// app/docs/[[...slug]]/page.tsx
export default async function DocsPage({ params }) {
const segments = params.slug ?? []; // [] for /docs itself
const path = segments.join('/');
const doc = await getDocByPath(path || 'index');
return <article dangerouslySetInnerHTML={{ __html: doc.html }} />;
}

FAQs

No — a catch-all segment must be the last part of the path in that folder chain; nothing can follow it.

Use a catch-all when the depth of the path is unknown or CMS-driven; use individual [segments] when the structure is fixed and predictable.

Summary

Catch-all segments handle arbitrarily deep paths, and the optional variant also covers the base path itself. Next, you'll look at parallel routes, which render more than one page in the same layout simultaneously.

Next Lesson →

Parallel Routes