Next.js 13+ introduced a new mental model: by default, all components are Server Components. You opt into client-side rendering with "use client".
Server Components render only on the server and ship zero JavaScript to the browser.
Benefits:
1// app/page.tsx — Server Component by default
2import { prisma } from "@/lib/prisma";
3
4export default async function HomePage() {
5 // Direct DB query — no API route needed!
6 const courses = await prisma.course.findMany({
7 where: { status: "PUBLISHED" },
8 take: 6,
9 });
10
11 return (
12 <main>
13 {courses.map(c => <CourseCard key={c.id} course={c} />)}
14 </main>
15 );
16}Client Components run in the browser and support interactivity.
1"use client"; // This directive opts the file into client rendering
2
3import { useState } from "react";
4
5export function LikeButton({ initialCount }: { initialCount: number }) {
6 const [count, setCount] = useState(initialCount);
7 return (
8 <button onClick={() => setCount(c => c + 1)}>
9 ❤️ {count}
10 </button>
11 );
12}Keep the server/client boundary as deep as possible.
Fetch data and prepare props in Server Components. Push interactivity (click handlers, state) down to small, leaf Client Components.
app/
page.tsx ← Server Component (fetches data)
CourseList.tsx ← Server Component (renders list)
CourseCard.tsx ← Server Component (renders card)
LikeButton.tsx ← "use client" (tiny, interactive)
| Feature | Server Component | Client Component |
|---|---|---|
| async/await at component level | ✅ | ❌ |
| Access DB / filesystem | ✅ | ❌ |
| useState / useEffect | ❌ | ✅ |
| Event listeners | ❌ | ✅ |
| Browser APIs | ❌ | ✅ |
| Cached by Next.js | ✅ | ❌ |