Server Components & React Server Components
Next.js 13+ Server Components: When to use Server vs Client components, data fetching patterns, streaming, Server Actions, hybrid architecture, performance benefits, and migration strategies.
๐ฅ๏ธ 1. Server vs Client Components
The million-dollar question: Server or Client? ๐ค Server Components run on the server (surprise!), ship zero JS to the browser, and can talk directly to your database. Client Components handle interactivity. Knowing when to use each is what separates the pros from the beginners!
๐ด Impact: CRITICAL โ Getting the Server vs Client split right can cut your bundle size by 50%+ and massively boost performance! ๐๏ธ
๐ In this section: Server Components (Default) โข 'use client' Directive โข Hybrid Architecture โข When to Use Which
// โ WRONG: Using client component for static data'use client';import { useState, useEffect } from 'react';function UserList() {const [users, setUsers] = useState([]);useEffect(() => {fetch('/api/users').then(res => res.json()).then(setUsers);}, []);return (<ul>{users.map(user => <li key={user.id}>{user.name}</li>)}</ul>);}
// โ CORRECT: Server Component (default)// app/users/page.tsx - No 'use client' directiveasync function UserList() {// This runs on the serverconst users = await fetchUsers();return (<ul>{users.map(user => (<li key={user.id}>{user.name}</li>))}</ul>);}// โ Client Component only when needed'use client';import { useState } from 'react';function InteractiveCounter() {const [count, setCount] = useState(0);return (<button onClick={() => setCount(count + 1)}>Count: {count}</button>);}// โ Hybrid: Server Component with Client Component// app/dashboard/page.tsx (Server Component)async function Dashboard() {const data = await fetchDashboardData();return (<div><ServerDataDisplay data={data} /><InteractiveChart data={data} /> {/* Client Component */}</div>);}
๐ก 2. Data Fetching Patterns
Remember the useEffect + useState + loading + error dance? ๐ With Server Components, you just... await your data. That's it. No loading states to manage, no race conditions to worry about, no waterfall requests. It's so simple it almost feels like cheating!
๐ Impact: HIGH โ Server-side data fetching eliminates entire categories of bugs and makes your code 10x simpler! ๐งน
๐ In this section: Async Server Components โข Direct DB Access โข No useEffect Needed โข Automatic Caching
// Server Component pattern (client demo with mock data)const MOCK_PRODUCTS = [{ id: '1', name: 'React Guide', price: 29.99 },{ id: '2', name: 'TypeScript Book', price: 34.99 },];function ProductList({ products }: { products: typeof MOCK_PRODUCTS }) {return (<ul>{products.map((p) => (<li key={p.id}>{p.name} โ ${p.price}</li>))}</ul>);}function ProductsPage() {const products = MOCK_PRODUCTS;return (<div><h1>Products</h1><ProductList products={products} /></div>);}function App() { return <ProductsPage />; }export default App;
๐ฌ 3. Server Actions
Server Actions are like magic wands for your forms! ๐ช Write a function, slap 'use server' on it, and boom โ it runs on the server when your form submits. No API routes, no fetch calls, no boilerplate. Just pure, beautiful simplicity. This is the future of form handling!
๐ด Impact: CRITICAL โ Server Actions replace entire API layers for mutations โ this is a paradigm shift in how we handle forms! ๐
๐ In this section: Form Submissions โข useTransition with Forms โข Optimistic Updates โข Progressive Enhancement
// Client demo: form + useTransition (mock server action)async function createUser(formData: FormData) {const name = formData.get('name') as string;const email = formData.get('email') as string;await new Promise((r) => setTimeout(r, 500));return { name, email };}function UserForm() {const [isPending, startTransition] = useTransition();const [result, setResult] = useState<string | null>(null);const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {e.preventDefault();const formData = new FormData(e.currentTarget);startTransition(async () => {const user = await createUser(formData);setResult(`Created: ${user.name} (${user.email})`);});};return (<div><form onSubmit={handleSubmit}><input name="name" placeholder="Name" required /><input name="email" type="email" placeholder="Email" required /><button type="submit" disabled={isPending}>{isPending ? 'Creating...' : 'Create User'}</button></form>{result && <p style={{ marginTop: 8 }}>{result}</p>}</div>);}function App() { return <UserForm />; }export default App;