Skip to content
C.W.K.
Stream
Lesson 01 of 09 · published

Server Component Fetching

~20 min · fetch, Server Component, dedupe

Level 0Curious
0 XP0/68 lessons0/11 achievements
0/120 XP to next level120 XP to go0% complete

You stop wiring data hooks

Server Components let you call fetch() or your DB client directly inside the component that renders the result. There's no useEffect, no isLoading state, no client-side React Query setup — the framework awaits and renders.

Where to fetch

Fetch in the component that needs the data, not at the page level. Drilling props through three layers fights the framework. Two siblings asking for the same URL with the same options trigger one network request — the request is deduped per render pass.

What this replaces

This pattern replaces React Query, SWR, and the Pages Router's getServerSideProps for the common case of "fetch on the server, render once." Client-side fetching libraries still earn their keep for live polling, infinite scroll, and offline caches; they just don't carry the weight anymore.

Separate data needed once for the initial document from data whose lifecycle belongs to the browser. Read the first category in a Server Component. Add a client cache for polling, infinite scroll, offline use, or user-driven refresh—not because every list traditionally starts with a hook. Fetching near the consumer does not mean duplicating policy in every leaf. Authorization, cache decisions, and error translation can live in a shared server function while each component calls that contract. Proximity and consistency are compatible.

Implement the initial list once with a client effect and once with a Server Component. Compare first-response HTML, request count, and JavaScript. If you hydrate a client cache from the server result, make sure it does not immediately fetch the same data again.

Code

Direct fetch·tsx
// app/posts/page.tsx
export default async function PostsPage() {
  const res = await fetch('https://api.example.com/posts', {
    next: { revalidate: 60 },
  });
  const posts = await res.json();
  return <PostList posts={posts} />;
}
Direct DB query·tsx
import { db } from '@/lib/db';

export default async function UsersPage() {
  const users = await db.user.findMany({
    include: { posts: true },
    orderBy: { createdAt: 'desc' },
  });
  return <UserTable users={users} />;
}
Each leaf fetches; framework dedupes identical calls·tsx
async function Header()  { const me = await fetch('/me');     return <Nav user={await me.json()} />; }
async function Sidebar() { const me = await fetch('/me');     return <UserCard user={await me.json()} />; }
// Both call /me; the framework only fetches once per render.

External links

Exercise

Take a page in your app that uses React Query or SWR for an initial server-rendered list. Replace it with a Server Component fetch and remove the client-side library dependency for that page.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.