Next.js 13 App Router: A Complete Guide
Next.js 13 App Router: A Complete Guide
The App Router is Next.js 13's revolutionary new routing system built on React Server Components.
App Directory Structure
app/
├── layout.tsx # Root layout
├── page.tsx # Home page
├── blog/
│ ├── layout.tsx # Blog layout
│ ├── page.tsx # Blog listing
│ └── [slug]/
│ └── page.tsx # Individual blog post
├── loading.tsx # Loading UI
├── error.tsx # Error UI
└── not-found.tsx # 404 page
Layouts
// app/layout.tsx
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'] });
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
<nav>Navigation</nav>
{children}
<footer>Footer</footer>
</body>
</html>
);
}
Pages
// app/page.tsx
async function getPosts() {
const res = await fetch('https://api.example.com/posts');
return res.json();
}
export default async function Home() {
const posts = await getPosts();
return (
<main>
<h1>Blog Posts</h1>
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</main>
);
}
Dynamic Routes
// app/blog/[slug]/page.tsx
async function getPost(slug: string) {
const res = await fetch(`https://api.example.com/posts/${slug}`);
return res.json();
}
export default async function PostPage({
params,
}: {
params: { slug: string };
}) {
const post = await getPost(params.slug);
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
);
}
// Generate static pages
export async function generateStaticParams() {
const posts = await getPosts();
return posts.map(post => ({ slug: post.slug }));
}
Loading States
// app/loading.tsx
export default function Loading() {
return (
<div className="animate-pulse">
<div className="h-8 bg-gray-200 rounded w-1/2 mb-4"></div>
<div className="h-4 bg-gray-200 rounded w-3/4 mb-2"></div>
<div className="h-4 bg-gray-200 rounded w-1/2"></div>
</div>
);
}
Error Handling
// app/error.tsx
'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>
);
}
Server Actions
// app/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
export async function createPost(formData: FormData) {
const title = formData.get('title');
await fetch('https://api.example.com/posts', {
method: 'POST',
body: JSON.stringify({ title }),
});
revalidatePath('/blog');
}
// app/blog/new/page.tsx
import { createPost } from '../actions';
export default function NewPost() {
return (
<form action={createPost}>
<input name="title" type="text" required />
<button type="submit">Create</button>
</form>
);
}
Route Groups
app/
├── (marketing)/
│ ├── about/
│ │ └── page.tsx
│ └── layout.tsx
├── (shop)/
│ ├── products/
│ │ └── page.tsx
│ └── layout.tsx
└── layout.tsx
Metadata
// Static metadata
export const metadata = {
title: 'My Blog',
description: 'A blog about web development',
};
// Dynamic metadata
export async function generateMetadata({ params }) {
const post = await getPost(params.slug);
return {
title: post.title,
description: post.excerpt,
};
}
Conclusion
The App Router brings powerful new capabilities to Next.js. Server Components, nested layouts, and streaming make building web applications more intuitive and performant.