Next.js: File-Based Routing Deep Dive
Leeting Yan
Next.js uses your folder structure as your routing system.
This can feel simple at first (“a folder becomes a URL”), but under the surface, file-based routing is one of the most expressive tools in modern web development.
In this chapter, we’ll take a calm, structured look at how routing works in the App Router, and how to use it to build clear, scalable, real-world applications.
No complicated abstractions — just practical, predictable patterns.
Why file-based routing works so well
Traditional routing systems use code-based configuration:
<Route path="/dashboard/settings" component={Settings} />
This becomes difficult to maintain in large apps.
File-based routing removes this complexity:
app/dashboard/settings/page.tsx → /dashboard/settings
The benefits:
- Routing is visible from your folder tree.
- URLs and UI structure stay in sync.
- Refactoring routes is safer and easier.
- Layouts and routing structure compose naturally.
Instead of managing a router, you simply build your UI tree.
1. Static routes
The simplest form:
app/
page.tsx → /
about/
page.tsx → /about
contact/
page.tsx → /contact
Static pages are ideal for:
- Marketing pages
- Product pages
- Documentation sections
- Help center articles
They produce fast, predictable URLs with no configuration.
2. Dynamic routes ([param])
Dynamic routes map patterns like /blog/:slug or /products/:id.
Example:
app/
blog/
[slug]/
page.tsx → /blog/:slug
Inside the page:
export default function Page({ params }) {
return <h1>{params.slug}</h1>;
}
You can capture any type of segment:
/products/123/blog/how-to-use-nextjs/user/john-doe
Dynamic routes keep your folder structure clean even as your content grows.
3. Catch-all routes ([...param])
Sometimes you don’t know how many segments a URL will have.
A catch-all route:
app/
docs/
[...slug]/
page.tsx → /docs/* (any depth)
Examples:
/docs/docs/getting-started/docs/api/v1/errors/docs/anything/here/or/deeper
params.slug becomes an array:
// /docs/api/v1
params.slug // ["api", "v1"]
This is powerful for:
- Documentation hierarchies
- Nested categories
- Custom router imports
- Legacy URL migrations
4. Optional catch-all routes ([[...param]])
If you want the root segment AND deeper segments handled by the same route:
app/
shop/
[[...filters]]/
page.tsx → /shop and /shop/* both match
Examples:
/shop/shop/category/shoes/shop/brand/nike
params.filters:
undefinedfor/shop- Array for deeper URLs
Useful for:
- Filter pages
- Search results
- Multi-level browsing
5. Nested routing and nested layouts
One of the App Router’s biggest strengths is deeply nested routing without losing layout structure.
app/
dashboard/
layout.tsx → wraps all dashboard pages
page.tsx → /dashboard
settings/
page.tsx → /dashboard/settings
analytics/
page.tsx → /dashboard/analytics
Each section inherits the layout above it.
This gives you:
- Persistent navigation
- Shared sidebar/header
- State that doesn’t reset on navigation
Perfect for dashboards and SaaS apps.
6. Route groups ((group))
Groups allow you to organize folders without affecting the URL.
app/
(marketing)/
about/
page.tsx → /about
contact/
page.tsx → /contact
The (marketing) folder is ignored in the URL.
This is extremely helpful for:
- separating marketing vs. app code
- grouping authenticated vs. public pages
- grouping experiments or feature flags
- keeping a clean top-level
app/structure
Another example:
app/
(app)/
dashboard/
page.tsx → /dashboard
(auth)/
login/
page.tsx → /login
Your filesystem can stay tidy without affecting your URLs.
7. Parallel routes (@slot)
Parallel routes let you render multiple segments at the same URL.
Example folder:
app/
dashboard/
layout.tsx
@feed/
page.tsx
@stats/
page.tsx
At /dashboard, both feed and stats content render into different layout slots.
Use cases:
- Dashboards showing multiple panels
- Split-screen UI
- Multi-feed interfaces
This is one of the more advanced App Router capabilities, but it enables powerful, modular UI.
8. Intercepting routes ((.), (..), (...))
Intercepting lets you temporarily replace part of the URL structure during navigation.
For example:
app/
inbox/
page.tsx
(.)message/[id]/page.tsx
This allows:
- Opening a message as a modal over
/inbox - Without navigating away from the inbox route
- While preserving browser navigation semantics
Ideal for:
- Overlays
- Modals
- Side panels
- Quick previews
A surprisingly elegant pattern once understood.
9. API routes with route.ts
Next.js 14 allows defining API endpoints inside the routing tree.
app/
api/
users/
route.ts → /api/users
Example:
export async function GET() {
return Response.json({ message: "Hello" });
}
Benefits:
- API lives next to the UI segment that uses it
- Easier to maintain
- No external server required for simple needs
For larger apps, you may still prefer dedicated backend services — but route handlers are great for small to medium projects.
10. Designing a scalable folder structure
A healthy folder tree should feel calm and predictable.
A recommended structure:
app/
(public)/
about/
pricing/
(auth)/
login/
signup/
(dashboard)/
layout.tsx
page.tsx
settings/
analytics/
components/
lib/
types/
Guidelines:
- Group by feature, not by file type
- Group related routes with route groups
- Avoid deeply nested complexity early on
- Split components out of routes once they grow
- Keep UI segments small and focused
Routing should feel like a map — easy to trace, easy to modify.
11. Practical tips
- Start simple. Add complexity only when needed.
- Prefer route groups to keep
app/tidy. - Move shared UI into layouts early.
- Don’t overuse dynamic routing — static is easier to cache and maintain.
- Use catch-all routes sparingly; they’re powerful but easy to misuse.
- Keep routing aligned with your product’s mental model.
The goal is not cleverness — it’s clarity.
Summary
Next.js file-based routing is more than a folder convention.
It is a structural foundation that shapes how your UI grows, how your team collaborates, and how your application scales.
By combining static routes, dynamic segments, nested layouts, route groups, and advanced patterns like parallel and intercepting routes, you can express almost any modern web application with a clean, predictable filesystem.
In the next chapter, we’ll shift focus to a core part of the App Router:
Server Components and Client Components — what runs where, and why it matters.
Understanding that boundary unlocks the full power of Next.js.