Next.js App Router — Complete Tutorial
Advertisement
Next.js App Router — Complete Tutorial
The App Router represents a fundamental shift in how Next.js applications are structured. This tutorial covers everything from basic routing to advanced patterns.
- Next.js App Router — Complete Tutorial
- Understanding File-Based Routing
- Basic Route Creation
- Dynamic Routes with Params
- Nested Layouts
- Route Groups for Organization
- Catch-All Routes
- Parallel Routes
- Intercepting Routes
- Linking Between Routes
- Programmatic Navigation
- Route Metadata
- FAQ
Understanding File-Based Routing
Next.js uses the file system as your router. Files in the app directory automatically become routes.
app/
├── page.tsx → /
├── about/
│ └── page.tsx → /about
├── blog/
│ ├── page.tsx → /blog
│ └── [slug]/
│ └── page.tsx → /blog/[slug]
└── api/
└── posts/
└── route.ts → POST /api/posts
Basic Route Creation
Creating a route is as simple as adding a file:
// app/about/page.tsx
export default function AboutPage() {
return (
<div>
<h1>About Us</h1>
<p>Learn more about our company.</p>
</div>
)
}
Visit /about and your component renders instantly.
Dynamic Routes with Params
Dynamic segments capture variable parts of URLs:
// app/blog/[slug]/page.tsx
interface Params {
slug: string
}
export default function BlogPost({ params }: { params: Params }) {
return (
<article>
<h1>Blog Post: {params.slug}</h1>
<p>Full post content loads here.</p>
</article>
)
}
A request to /blog/hello-world passes slug: "hello-world" to the component.
Nested Layouts
Layouts wrap multiple pages with shared UI:
// app/blog/layout.tsx
export default function BlogLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<div className="flex gap-8">
<aside className="w-48">
<nav>
<a href="/blog">All Posts</a>
<a href="/blog/tips">Tips</a>
<a href="/blog/tutorials">Tutorials</a>
</nav>
</aside>
<main className="flex-1">{children}</main>
</div>
)
}
This layout applies to /blog and all nested routes.
Route Groups for Organization
Parentheses organize routes without affecting URLs:
app/
├── (marketing)/
│ ├── page.tsx → /
│ ├── about/
│ │ └── page.tsx → /about
│ └── layout.tsx → shared layout
├── (dashboard)/
│ ├── layout.tsx → different layout
│ ├── page.tsx → /
│ └── analytics/
│ └── page.tsx → /analytics
Catch-All Routes
Triple dots capture all remaining segments:
// app/docs/[[...slug]]/page.tsx
interface Params {
slug?: string[]
}
export default function DocsPage({ params }: { params: Params }) {
const path = params.slug?.join('/') || 'home'
return <div>Viewing docs: {path}</div>
}
Both /docs and /docs/guides/getting-started match this route.
Parallel Routes
Render multiple segments simultaneously:
// app/@modal/(..)post/[id]/page.tsx
// app/@sidebar/page.tsx
// app/layout.tsx
export default function Layout({
children,
modal,
sidebar,
}: {
children: React.ReactNode
modal: React.ReactNode
sidebar: React.ReactNode
}) {
return (
<div>
<div className="flex">
{sidebar}
<main>{children}</main>
</div>
{modal}
</div>
)
}
Intercepting Routes
Intercept routes to show content in modals:
// app/(.)post/[id]/page.tsx
export default function PostModal({ params }: { params: { id: string } }) {
return (
<div className="modal">
<h2>Post {params.id}</h2>
<p>Rendered as a modal when navigated from certain pages.</p>
</div>
)
}
Linking Between Routes
Use the Link component for client-side navigation:
import Link from 'next/link'
export default function Navigation() {
return (
<nav>
<Link href="/">Home</Link>
<Link href="/about">About</Link>
<Link href="/blog/hello-world">Blog Post</Link>
</nav>
)
}
Programmatic Navigation
Use the useRouter hook for dynamic navigation:
'use client'
import { useRouter } from 'next/navigation'
export default function LoginForm() {
const router = useRouter()
async function handleSubmit(e: FormData) {
const user = await fetch('/api/login', { method: 'POST', body: e })
if (user.ok) {
router.push('/dashboard')
}
}
return <form action={handleSubmit}>...</form>
}
Route Metadata
Define page metadata for SEO:
// app/blog/[slug]/page.tsx
export const metadata = {
title: 'Blog Post',
description: 'Read our latest blog post',
}
export default function Page() {
return <article>...</article>
}
FAQ
Q: How do I handle 404 pages? A: Create a not-found.tsx file in any directory. Next.js shows this when a route doesn't match.
Q: Can I use regex in dynamic routes? A: No, but you can use catch-all routes [[...slug]] and validate segments in your component.
Q: What's the difference between /app/blog/[id] and /app/blog/(post)/[id]? A: The first creates a route /blog/[id]. The second with parentheses also creates /blog/[id] but groups files without affecting the URL structure. Use groups to organize related pages.
Master the App Router and you've mastered Next.js's core routing system.
Advertisement