File-Based Routing Deep Dive
Next.js uses the file system as the source of truth for routes. Understanding the conventions unlocks powerful routing patterns.
Basic Routes
Folders define route segments. A page.tsx inside makes it accessible:
app/
āāā page.tsx ā /
āāā about/
ā āāā page.tsx ā /about
āāā blog/
ā āāā page.tsx ā /blog
āāā contact/
āāā page.tsx ā /contact
Dynamic Routes
Use square brackets for dynamic segments:
app/
āāā blog/
ā āāā [slug]/
ā āāā page.tsx ā /blog/my-first-post
āāā users/
ā āāā [id]/
ā āāā page.tsx ā /users/123
Access parameters in your component:
// app/blog/[slug]/page.tsx
export default async function BlogPost({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
return <h1>Post: {slug}</h1>
}
Catch-All Routes
Use [...slug] to catch multiple segments:
app/
āāā docs/
āāā [...slug]/
āāā page.tsx ā /docs/a, /docs/a/b, /docs/a/b/c
// params.slug = ['a', 'b', 'c'] for /docs/a/b/c
export default async function Docs({
params,
}: {
params: Promise<{ slug: string[] }>
}) {
const { slug } = await params
return <div>Path: {slug.join('/')}</div>
}
Use [[...slug]] for optional catch-all (also matches the root):
app/
āāā shop/
āāā [[...categories]]/
āāā page.tsx ā /shop, /shop/clothes, /shop/clothes/shirts
Static Params Generation
For dynamic routes, tell Next.js which pages to pre-generate:
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await getPosts()
return posts.map((post) => ({
slug: post.slug,
}))
}
Colocation
You can colocate files that aren't special (page, layout, etc.):
app/
āāā dashboard/
āāā page.tsx # The route
āāā Dashboard.tsx # Component (not a route)
āāā dashboard.css # Styles
āāā utils.ts # Helpers
Only page.tsx, layout.tsx, and other special files affect routing.
Private Folders
Prefix with underscore to exclude from routing:
app/
āāā _components/ # Shared components (not routes)
ā āāā Button.tsx
āāā _lib/ # Utilities (not routes)
ā āāā api.ts
āāā dashboard/
āāā page.tsx # Actual route
Route Priority
When routes could conflict, Next.js uses this priority:
- Static routes (
/about) - Dynamic routes (
/[slug]) - Catch-all routes (
/[...slug])
app/
āāā blog/
ā āāā page.tsx # /blog (static, wins)
ā āāā latest/
ā ā āāā page.tsx # /blog/latest (static, wins)
ā āāā [slug]/
ā āāā page.tsx # /blog/anything-else (dynamic)
Summary
- Folders define routes,
page.tsxmakes them accessible [param]for single dynamic segments[...param]for catch-all,[[...param]]for optional catch-allgenerateStaticParamspre-generates dynamic routes- Underscore prefix (
_folder) excludes from routing - Static routes take priority over dynamic ones

