App Router Fundamentals
The App Router is Next.js's file-system based router with built-in support for layouts, server components, streaming, and more.
File-System Routing
app/
โโโ layout.tsx # Root layout (required)
โโโ page.tsx # Home page (/)
โโโ about/
โ โโโ page.tsx # About page (/about)
โโโ blog/
โ โโโ page.tsx # Blog list (/blog)
โ โโโ [slug]/
โ โโโ page.tsx # Blog post (/blog/my-post)
โโโ (marketing)/ # Route group (no URL impact)
โ โโโ layout.tsx # Marketing pages layout
โ โโโ pricing/
โ โ โโโ page.tsx # /pricing
โ โโโ features/
โ โโโ page.tsx # /features
โโโ @modal/ # Parallel route for modals
โโโ login/
โโโ page.tsxLayouts and Templates
// app/layout.tsx - Root layout export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ({children}
Loading UI and Streaming
// app/dashboard/loading.tsx export default function Loading() { return (); } // Streaming with Suspense import { Suspense } from 'react'; async function DashboardPage() { return (); } // Each component can fetch independently async function Stats() { const stats = await fetchStats(); // No loading state blocking returnDashboard
{/* These components stream in as they resolve */}}> }> }> ; }
Error Handling
// app/dashboard/error.tsx
'use client';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
Something went wrong!
{error.message}
);
}
// app/not-found.tsx
export default function NotFound() {
return (
404 - Page Not Found
Go home
);
}Data Fetching
// Server Component - Direct data access
async function ProductsPage() {
// This runs on the server
const products = await db.product.findMany();
return (
{products.map(product => (
- {product.name}
))}
);
}
// Caching and revalidation
async function Posts() {
// Cached indefinitely (default)
const posts = await fetch('https://api.example.com/posts');
// Revalidate every hour
const news = await fetch('https://api.example.com/news', {
next: { revalidate: 3600 }
});
// No caching
const live = await fetch('https://api.example.com/live', {
cache: 'no-store'
});
return (...);
}Server Actions
// app/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
const content = formData.get('content') as string;
// Validation
if (!title || !content) {
return { error: 'Title and content required' };
}
// Create in database
const post = await db.post.create({
data: { title, content }
});
// Revalidate and redirect
revalidatePath('/posts');
redirect(`/posts/${post.id}`);
}
// Using in form
export default function NewPostForm() {
return (
);
}Middleware
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// Check authentication
const token = request.cookies.get('token');
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
// Add headers
const response = NextResponse.next();
response.headers.set('x-request-id', crypto.randomUUID());
return response;
}
export const config = {
matcher: ['/dashboard/:path*', '/api/:path*']
};Conclusion
The App Router brings React Server Components to Next.js with file-based routing, layouts, and streaming. Embrace server-first rendering for better performance and simpler code.