Catch-All Routes
Match multiple URL segments with [...name] and optional [[...name]] - params as arrays.
Overview
Catch-all routes match multiple URL segments in one route using [...name]. The param becomes an array. The optional version [[...name]] also matches the parent path. Each creates a URL.
| Pattern | Example URL | Creates URL? |
|---|---|---|
| [...slug] | /docs/api/reference | Yes - catch-all creates URL |
| [[...slug]] | /shop and /shop/clothing | Yes - optional catch-all creates URL |
useParams() is auto-imported - no import needed to access catch-all params as an array.What are Catch-All Routes?
Catch-all routes match any number of segments after the parent path using [...name]. The param is an array of matched segments. Lower priority than static and single dynamic routes.
- Matches multiple segments: any number after the parent path
- Array parameter: param becomes an array of segments
- Lower priority: static and
[slug]are matched first - Optional version:
[[...name]]for optional catch-all
/docs/getting-started matches with slug = ['getting-started'], /docs/guides/routing/basics with slug = ['guides', 'routing', 'basics'].
Basic Usage
Create a catch-all by naming a folder or file [...name]. Requires at least one segment. Creates a URL for any depth.
[...slug] requires at least one segment. Use [[...slug]] for an optional catch-all that also matches the parent.Accessing Parameters
Use useParams() (auto-imported). The param is an array.
export default function DocsPage() {
const { slug } = useParams()
// slug is array of URL segments
return (
<div>
<h1>Documentation</h1>
<p>Path: {slug?.join(' / ')}</p>
<p>Depth: {slug?.length || 0}</p>
</div>
)
}| URL | slug value | Creates URL? |
|---|---|---|
| /docs/getting-started | ['getting-started'] | Yes |
| /docs/api/reference | ['api', 'reference'] | Yes |
| /docs/guides/routing/basics | ['guides', 'routing', 'basics'] | Yes |
| /docs/advanced/custom/hooks | ['advanced', 'custom', 'hooks'] | Yes |
export default function BlogArchive() {
const { slug } = useParams()
const [posts, setPosts] = useState([])
useEffect(() => {
const path = slug?.join('/')
fetchPosts(path).then(setPosts)
}, [slug])
return (
<div>
<h1>Archive: {slug?.join(' / ') || 'Home'}</h1>
{posts.map(post => (
<div key={post.id}>{post.title}</div>
))}
</div>
)
}Nested Catch-All Routes
Combine catch-all with static and dynamic segments. Each combination creates a URL.
export default function ProductPage() {
const { category, slug } = useParams()
return (
<div>
<h1>Category: {category}</h1>
<p>Path: {slug?.join(' / ')}</p>
<p>Segments: {slug?.length || 0}</p>
</div>
)
}Optional Catch-All Routes
[[...name]] makes the catch-all optional - it matches the parent and nested paths. Creates a URL for both.
export default function ShopPage() {
const { slug } = useParams()
if (!slug || slug.length === 0) {
return <h1>Shop Home</h1>
}
return (
<div>
<h1>Category: {slug.join(' / ')}</h1>
<p>Depth: {slug.length}</p>
</div>
)
}| URL | slug value | Creates URL? |
|---|---|---|
| /shop | undefined | Yes - parent creates URL |
| /shop/clothing | ['clothing'] | Yes |
| /shop/clothing/shirts | ['clothing', 'shirts'] | Yes |
| /docs | undefined | Yes - parent creates URL |
| /docs/getting-started | ['getting-started'] | Yes |
/docs shows a landing page and /docs/getting-started shows content.File-Based Catch-All Routes
Catch-all as flat files without folders. Creates a URL directly and reduces nesting.
export default function BlogArchive() {
const { slug } = useParams()
return <h1>Archive: {slug?.join(' / ')}</h1>
}Route Priority
Catch-all has lower priority than static and single dynamic routes. All create URLs, but static wins.
- Static routes -
/blog/featured- creates URL, highest priority - Dynamic single -
[slug]→/blog/:slug- creates URL - Catch-all -
[...slug]→/blog/*- creates URL - Optional catch-all -
[[...slug]]→/docs/*- creates URL, lowest
| URL | Matched Route | Creates URL? |
|---|---|---|
| /blog/featured | featured/page.tsx - static | Yes |
| /blog/hello-world | [slug]/page.tsx - dynamic | Yes |
| /blog/2024/01/hello-world | [...slug]/page.tsx - catch-all | Yes |
Use Cases
Catch-all routes are ideal for multi-segment structures. All create URLs.
/docs/guides/routing/basics/products/electronics/phones/iphone/blog/2024/01/hello-world/en/docs/getting-started| Use Case | Example URL | Creates URL? |
|---|---|---|
| CMS Content | /wiki/guides/routing | Yes |
| API Versioning | /api/v1/users/123 | Yes |
| File Browser | /files/docs/guides | Yes |
| Wiki Pages | /wiki/guides/routing/basics | Yes |
Complete Example
Catch-all only - [...param] and [[...param]] patterns that create URLs.
| Pattern | Example URL | Creates URL? |
|---|---|---|
| /blog/featured | /blog/featured | Yes - static creates URL |
| /blog/:slug | /blog/hello-world | Yes - dynamic creates URL |
| /blog/* | /blog/2024/01/hello-world | Yes - catch-all creates URL |
| /docs/* (optional) | /docs | Yes - optional catch-all creates URL |
| /products/:category/* | /products/electronics/phones/iphone | Yes - nested catch-all creates URL |
| /shop/* (optional) | /shop/clothing/shirts | Yes - optional catch-all creates URL |
| /api/v1/* | /api/v1/users/123 | Yes - flat file catch-all creates URL |