Next.js 16 App Router
Expert guidance for building modern Next.js 16 applications with App Router.
Quick Decision: Server or Client Component?
┌─────────────────────────────────────────────────────────────┐
│ Component Decision Tree │
├─────────────────────────────────────────────────────────────┤
│ │
│ Does it need interactivity (onClick, onChange, etc.)? │
│ ├── YES → Client Component ("use client") │
│ └── NO ↓ │
│ │
│ Does it use React hooks (useState, useEffect, etc.)? │
│ ├── YES → Client Component ("use client") │
│ └── NO ↓ │
│ │
│ Does it need browser APIs (localStorage, window)? │
│ ├── YES → Client Component ("use client") │
│ └── NO → Server Component (default, no directive needed) │
│ │
└─────────────────────────────────────────────────────────────┘
Server Component (Default)
// app/tasks/page.tsx - NO "use client" directive
import { getTasks } from "@/lib/api";
export default async function TasksPage() {
const tasks = await getTasks(); // Direct async data fetching
return (
<main>
<h1>Tasks</h1>
<ul>
{tasks.map((task) => (
<li key={task.id}>{task.title}</li>
))}
</ul>
</main>
);
}
Client Component
"use client"; // Required directive at top of file
import { useState } from "react";
export function TaskForm({ onSubmit }: { onSubmit: (title: string) => void }) {
const [title, setTitle] = useState("");
return (
<form => { e.preventDefault(); onSubmit(title); }}>
<input
value={title}
=> setTitle(e.target.value)}
placeholder="Task title"
/>
<button type="submit">Add Task</button>
</form>
);
}
Server Action (Mutation)
// app/tasks/actions.ts
"use server";
import { revalidatePath } from "next/cache";
export async function createTask(formData: FormData) {
const title = formData.get("title") as string;
await fetch(`${process.env.API_URL}/tasks`, {
method: "POST",
body: JSON.stringify({ title }),
});
revalidatePath("/tasks"); // Refresh the page data
}
// app/tasks/page.tsx - Using the action
import { createTask } from "./actions";
export default function TasksPage() {
return (
<form action={createTask}>
<input name="title" placeholder="Task title" />
<button type="submit">Add Task</button>
</form>
);
}
Project Structure
app/
├── layout.tsx # Root layout (Server Component)
├── page.tsx # Home page
├── globals.css # Global styles
├── tasks/
│ ├── page.tsx # /tasks route
│ ├── actions.ts # Server Actions
│ ├── [id]/
│ │ └── page.tsx # /tasks/:id route
│ └── components/
│ └── TaskForm.tsx # Client Component
├── api/
│ └── tasks/
│ └── route.ts # API route handler
└── auth/
└── [...nextauth]/
└── route.ts # Better Auth handler
Reference Guides
For detailed patterns, see:
- App Router: See references/app-router.md for routing, layouts, loading states, and error handling
- Server/Client Components: See references/server-client-components.md for composition patterns and data fetching
- Server Actions: See references/server-actions.md for mutations, validation, and optimistic updates
- Better Auth: See references/better-auth.md for authentication setup and protected routes
- API Routes: See references/api-routes.md for route handlers and middleware
- React 19 Patterns: See references/react-19-patterns.md for useFormStatus, useActionState, and transitions