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

