---
name: nextjs-app-router
description: Build modern Next.js 13+ applications using App Router architecture with Server Components, Client Components, and advanced routing patterns. Use when implementing server-first rendering, creating nested layouts, building parallel routes, implementing intercepting routes, using React Server Components, optimizing data fetching with server-side async components, implementing streaming with Suspense, managing client-side interactivity boundaries, or leveraging Next.js 13+ app directory features for performant, SEO-friendly React applications.
---
# Next.js App Router - Modern React Framework Patterns
## When to use this skill
- Building Next.js 13+ applications with App Router
- Implementing Server Components for server-first rendering
- Creating Client Components for interactive UI elements
- Setting up nested layouts that persist across routes
- Implementing parallel routes for complex UI patterns
- Using intercepting routes for modals and overlays
- Optimizing data fetching with async Server Components
- Implementing streaming and progressive rendering with Suspense
- Managing client/server component boundaries effectively
- Building SEO-friendly applications with server-side rendering
- Leveraging server actions for form handling
- Creating file-based routing with app directory structure
## When to use this skill
- Building Next.js 13+ applications with App Router, implementing Server Components, or optimizing React Server Components patterns.
- When working on related tasks or features
- During development that requires this expertise
**Use when**: Building Next.js 13+ applications with App Router, implementing Server Components, or optimizing React Server Components patterns.
## Core Concepts
1. **Server Components by Default** - Components are server-rendered unless marked with 'use client'
2. **File-based Routing** - `/app` directory structure defines routes
3. **Streaming & Suspense** - Progressive rendering for better UX
4. **Data Fetching** - Server-side with automatic caching
5. **Layouts & Templates** - Shared UI that persists across routes
## App Router Structure
```
app/
├── layout.tsx # Root layout (required)
├── page.tsx # Home page (/)
├── loading.tsx # Loading UI
├── error.tsx # Error UI
├── not-found.tsx # 404 UI
├── dashboard/
│ ├── layout.tsx # Dashboard layout
│ ├── page.tsx # /dashboard
│ └── settings/
│ └── page.tsx # /dashboard/settings
└── api/
└── users/
└── route.ts # API endpoint /api/users
```
## Server vs Client Components
```typescript
// ✅ Server Component (default - no 'use client')
// app/page.tsx
import { db } from '@/lib/db';
export default async function Page() {
// Fetch data on server
const users = await db.user.findMany();
return (
Users
{users.map(user => )}
);
}
// ✅ Client Component (interactive features)
// app/components/Counter.tsx
'use client';
import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return (
);
}
// ✅ Composition: Server Component with Client Component children
// app/page.tsx
import { Counter } from './components/Counter';
export default async function Page() {
const data = await fetchData(); // Server-side
return (
Server Data: {data}
{/* Client interactive component */}
);
}
```
## Data Fetching
```typescript
// ✅ Fetch with caching (default: 'force-cache')
async function getUsers() {
const res = await fetch('https://api.example.com/users', {
cache: 'force-cache' // Cached until revalidated
});
return res.json();
}
// ✅ Revalidate every 60 seconds
async function getUsers() {
const res = await fetch('https://api.example.com/users', {
next: { revalidate: 60 }
});
return res.json();
}
// ✅ No caching (always fresh)
async function getUsers() {
const res = await fetch('https://api.example.com/users', {
cache: 'no-store'
});
return res.json();
}
// ✅ Database query (Prisma)
import { db } from '@/lib/db';
async function getUser(id: string) {
return await db.user.findUnique({ where: { id } });
}
```
## Dynamic Routes
```typescript
// app/posts/[id]/page.tsx
interface PageProps {
params: { id: string };
searchParams: { [key: string]: string | string[] | undefined };
}
export default async function PostPage({ params }: PageProps) {
const post = await db.post.findUnique({ where: { id: params.id } });
return