Now that you understand the basics of the App Router, it’s time to dive deeper into one of the most powerful aspects of Next.js: Routing. Unlike traditional React—where routing requires libraries like React Router—Next.js uses file-based routing, which is simpler, faster, and more intuitive.
In this guide, you’ll learn:
Let’s get started.
Next.js maps your folder structure directly to URLs.
Inside the app/ folder:
page.tsx = page contentFor example:
app/├── page.tsx → '/'├── about/│ └── page.tsx → '/about'
To create a new route like /about, just:
aboutpage.tsx inside itNext.js automatically makes it available at:
http://localhost:3000/about
No configuration, no routers, no boilerplate.
Let’s imagine you’re building an admin dashboard with routes like:
/dashboard/users/dashboard/analyticsTo do this:
app/├── dashboard/│ ├── users/│ │ └── page.tsx│ ├── analytics/│ │ └── page.tsx
Each folder represents a nested route. It’s simple, clean, and extremely scalable.
What if you want a route like:
/dashboard/users/1/dashboard/users/2/dashboard/users/92301You don’t create a folder for every user.
Instead, create a dynamic folder:
app/├── dashboard/│ └── users/│ └── [id]/│ └── page.tsx
The [id] folder tells Next.js:
“This part of the URL is dynamic.”
Inside page.tsx, you can access the ID using params.
Example:
export default async function UserPage({ params }: { params: Promise<{ id: string }> }) {const { id } = await params;return <h1>Showing details for user {id}</h1>;}
This is how you build dynamic, data-driven pages in Next.js.
Use the built-in Link component:
import Link from "next/link";<Link href="/dashboard/users/1">User 1</Link>
By default, Next.js:
layout.tsx files let you share UI across multiple routes.
Example root layout:
export default function RootLayout({ children }: { children: React.ReactNode }) {return (<html><body><nav>Navbar</nav>{children}<footer>Footer</footer></body></html>);}
Whatever you put in a layout:
app/├── dashboard/│ └── layout.tsx
Inside this layout, you may add a dashboard navbar:
<p>Dashboard Navbar</p>{children}
This layout applies only to dashboard pages.
Now suppose you want:
But you don’t want the folder names to appear in the URL.
This is where route groups help.
Create folders wrapped in parentheses:
app/├── (root)/│ ├── page.tsx│ ├── about/│ └── layout.tsx ← public layout├── (dashboard)/│ ├── dashboard/│ │ ├── users/│ │ ├── analytics/│ │ └── layout.tsx ← dashboard layout
The parentheses tell Next.js:
“Use this folder for organization and layouts, but DON’T add it to the URL.”
This lets you structure your project however you want without affecting routing.
Next.js 16 introduced:
If multiple prefetches share the same layout…
➡ Next.js downloads that layout only once.
This reduces bandwidth and speeds up navigation automatically.
No code changes required.
error.tsxNext.js provides built-in error boundaries.
Just create an error.tsx inside any route folder:
app/├── (root)/│ ├── error.tsx
The error file must be a client component:
"use client";export default function Error({ error, reset }: { error: Error; reset: () => void }) {return (<div><h2>Something went wrong!</h2><button onClick={() => reset()}>Try again</button></div>);}
error.tsx handles the errorglobal-error.tsxThis keeps your app stable and user-friendly.
loading.tsxTo show a loading spinner or skeleton UI while data loads, create:
app/└── loading.tsx
Example:
export default function Loading() {return <p>Loading...</p>;}
This automatically displays anytime a route’s data is being fetched.
Next.js 16 adds two special files:
forbidden.tsxunauthorized.tsxUse these when:
Instead of an error or redirect, you can provide a clear message or UI.
You now understand:
✔ Page routing
✔ Nested routing
✔ Dynamic routing with params
✔ Layouts (root + nested)
✔ Route groups
✔ Navigation with <Link>
✔ Error handling
✔ Loading states
✔ Next.js 16 routing improvements
This is the foundation of how real-world Next.js applications are structured and scaled.