Two Worlds: Server and Client
Next.js App Router runs React in two environments:
| Feature | Server Component | Client Component |
|---|---|---|
| Runs on | Server (Node.js) | Browser (JavaScript) |
| async/await | ✅ Yes | ❌ No (needs useEffect) |
| useState/useEffect | ❌ No | ✅ Yes |
| Event handlers | ❌ No (onClick etc.) | ✅ Yes |
| DB access | ✅ Direct | ❌ Via API only |
| API keys | ✅ Safe | ❌ Exposed risk |
| Bundle size | ✅ Zero (stays on server) | Adds to bundle |
The "use client" Directive
All components are Server Components by default. Add this to the top of any file to make it a Client Component:
"use client";
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
Composition Pattern
You can put Client Components inside Server Components (not the other way around):
// app/page.tsx — Server Component
import Counter from "@/components/Counter"; // Client Component
import { fetchData } from "@/lib/api";
export default async function Page() {
const data = await fetchData(); // Runs on server
return (
<div>
<h1>{data.title}</h1>
<Counter /> {/* Client Component */}
</div>
);
}
Common Mistakes
❌ Mistake: Trying to use useState in a Server Component
// ❌ WRONG — doesn't work in Server Component
export default async function Page() {
const [open, setOpen] = useState(false); // ERROR!
return <div>...</div>;
}
✅ Solution: Move the interactive part to a separate Client Component file.
Decision Guide
- Use Server Component: Data fetching, DB queries, API keys, static rendering
- Use Client Component: onClick/onChange, useState/useEffect, browser APIs (localStorage, window), animations
Önemli Noktalar
- All components are Server Components by default
- "use client" makes a file a Client Component
- Client Components can be nested inside Server Components
- Minimise Client Components — smaller bundle, better performance