| name | data-fetching |
| description | Data fetching patterns for Next.js App Router — Server Components, Server Actions,
React Query, and streaming. Use when implementing or reviewing data fetching logic,
choosing between RSC and client-side fetching, or designing API routes.
|
Data Fetching (Next.js)
Canonical patterns for fetching data in Next.js App Router.
Fetch Strategy Decision
Need data for a page / route?
├─ Data is public / user-agnostic?
│ ├─ Static (rarely changes) → Server Component + fetch() with { cache: 'force-cache' }
│ ├─ Dynamic (per-request) → Server Component + fetch() (default dynamic)
│ └─ Revalidating → Server Component + fetch() with { next: { revalidate: N } }
├─ Data is user-specific?
│ └─ Server Component + cookies()/headers() (auto-deduped, secure)
├─ Need real-time / polling?
│ └─ Client Component + React Query / SWR
└─ Need user-initiated mutation?
└─ Server Action (form action / event handler)
Patterns
1. Server Component Data Fetching (Preferred)
export default async function PostsPage() {
const posts = await fetch('https://api.example.com/posts', {
next: { revalidate: 60 }
}).then(r => r.json())
return <PostList posts={posts} />
}
2. Parallel Data Fetching
export default async function Dashboard() {
const [user, analytics] = await Promise.all([
getUser(),
getAnalytics()
])
return <DashboardView user={user} analytics={analytics} />
}
3. Streaming with Suspense
import { Suspense } from 'react'
export default function Page() {
return (
<div>
<Header /> {/* Shown instantly */}
<Suspense fallback={<Skeleton />}>
<SlowDataComponent /> {/* Streamed in when ready */}
</Suspense>
</div>
)
}
4. Server Actions (Mutations)
'use server'
export async function createPost(formData: FormData) {
const title = formData.get('title')
await db.post.create({ title })
revalidatePath('/posts')
}
5. Client-Side React Query (Real-time / Polling)
When Server Components aren't enough (polling, real-time, window-focus refetch):
- Use React Query / TanStack Query
- Keep the fetch logic in a custom hook
- Use
initialData from Server Components to hydrate
Anti-Patterns
| Anti-Pattern | Why | Fix |
|---|
Fetching in useEffect for initial page data | Wastes server capability, hurts UX | Use Server Component fetching |
| Sequential fetches in Server Components | Doubles load time | Use Promise.all() |
| Mixing server/client fetch for the same data | Redundant network calls | Fetch once in Server, pass down |
| Over-fetching in RSC | Large bundle from serialized data | Only pass what the client needs |
useEffect + fetch for mutations | No built-in loading/error states | Use Server Actions + useActionState |
Checklist