The Problem Server Components Solve

If you've been building Django or FastAPI backends and wiring them to a React frontend, you know the pain: you write an API endpoint, then write a React component that fetches from it, then handle loading states, error states, caching...

Server Components let you skip the middle layer entirely for data fetching. The component runs on the server, has direct database access, and sends rendered HTML (with React hydration hints) to the client.

What Changes

// Before: Client Component - must fetch via API
"use client";
export function CourseList() {
  const [courses, setCourses] = useState([]);
  useEffect(() => {
    fetch("/api/courses/").then(r => r.json()).then(setCourses);
  }, []);
  return <ul>{courses.map(c => <li key={c.id}>{c.title}</li>)}</ul>;
}

// After: Server Component - direct DB access, no API needed
import { db } from "@/lib/db";
export async function CourseList() {
  const courses = await db.query("SELECT * FROM courses WHERE published = true");
  return <ul>{courses.map(c => <li key={c.id}>{c.title}</li>)}</ul>;
}

When NOT to Use Them

Server Components cannot use browser APIs, hooks like useState/useEffect, or event handlers. For interactive parts of your UI - forms, modals, real-time updates - you still need Client Components.

The pattern I use: Server Components for the "shell" and data loading, Client Components for the interactive bits.

Integration with Django

If your backend is Django, you have two options:

  1. Keep Django as the API - Next.js Server Components call your Django REST endpoints server-to-server (no CORS, faster).
  2. Replace Django for data fetching - use a Node.js ORM like Prisma alongside Next.js, with Django only handling business logic.

For the NHIA platform, I went with option 1 and saw a 40% reduction in API round trips.