Skill: nextjs
Scope
- Applies to: Next.js 16 App Router - Server Components, Client Components, API routes, Server Actions, middleware, caching
- Does NOT cover: Pages Router, deployment specifics, framework internals
Assumptions
- Next.js 16+
- Node.js 20.9+
- TypeScript 5.1+
- React 19+ (for full feature support)
- Turbopack (stable, default bundler)
Principles
- Use Server Components by default (async/await directly in components)
- Use Client Components (
'use client') only for interactivity (hooks, event handlers) - Use Server Actions for mutations (
'use server') - Use
use cache directive for explicit component-level caching (Next.js 16) - Use
revalidatePath and revalidateTag for cache invalidation - Use Suspense boundaries for async data loading
- Use
useTransition for non-urgent updates - Use
useDeferredValue for deferring expensive computations - Route handlers use
route.ts files with named exports (GET, POST, etc.)
Constraints
MUST
- Use Server Components by default (no
'use client' unless needed) - Mark Server Actions with
'use server' directive - Use
revalidatePath or revalidateTag after mutations - Handle
searchParams as Promise in Server Components (Next.js 15+)
SHOULD
- Use
useTransition for heavy updates that shouldn't block UI - Use
useDeferredValue with useMemo only for expensive operations (1000+ items) - Use Suspense boundaries for progressive loading
- Use route groups
(group) for organization without affecting URLs
AVOID
- Unnecessary
useMemo/useCallback (React 19 handles most optimizations) - Client Components for static content
- Mixing Server and Client Component patterns incorrectly
- React Compiler (wait for better ecosystem support)
Interactions
Patterns
Server Component
// app/page.tsx
export default async function Page() {
const data = await fetchData()
return <div>{data}</div>
}
Client Component
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
Server Action
'use server'
import { revalidatePath } from 'next/cache'
export async function createItem(formData: FormData) {
await createItemInDB(formData)
revalidatePath('/items')
}
Route Handler
// app/api/items/route.ts
export async function GET() {
return Response.json({ items: [] })
}
export async function POST(request: Request) {
const data = await request.json()
return Response.json({ created: data })
}
Caching
'use cache'
export async function CachedComponent() {
const data = await fetchData() // Cached at component level
return <div>{data}</div>
}
References