Loading content…
Loading content…
Learn React Server Components (RSC) paradigm, hydration, client boundaries, and modern data fetching patterns
"use client" directive at the top of the file.| Feature | Server Components | Client Components ("use client") |
|---|---|---|
| State & Hooks | ❌ No (useState, useEffect) | ✅ Yes |
| Browser APIs | ❌ No (window, localStorage) | ✅ Yes |
| Backend Access | ✅ Yes (DB query, File system) | ❌ No |
| Secrets | ✅ Yes (API Keys, private variables) | ❌ No (Could leak to public) |
| Dependencies | ✅ Yes (Kept on server bundle) | ❌ No (Downloaded by client browser) |
children prop.// ✅ Correct Pattern
// ServerComponent.tsx
import { ClientComponent } from "./ClientComponent";
import { StaticComponent } from "./StaticComponent";
export default function ServerComponent() {
return (
<ClientComponent>
<StaticComponent /> {/* Passed as children prop */}
</ClientComponent>
);
}
// ❌ Error: Cannot pass functions across the boundary!
<ClientComponent onClick={() => console.log('click')} />
window.innerWidth, localStorage) or dynamic elements like new Date() directly in renders.useEffect so they only execute after hydration, or disable SSR for that specific component.// ❌ Hydration Mismatch Risk
const DateComponent = () => {
return <p>{new Date().toLocaleTimeString()}</p>; // Server time differs from client time!
};
// ✅ Safe Pattern
const DateComponentSafe = () => {
const [time, setTime] = useState("");
useEffect(() => {
setTime(new Date().toLocaleTimeString());
}, []);
return <p>{time}</p>;
};
async/await directly inside the component body:// db.ts - Mock database call
export async function getProducts() {
const res = await fetch("https://api.example.com/products", {
next: { revalidate: 3600 } // Cache and revalidate every hour
});
return res.json();
}
// ProductCatalog.tsx (Server Component)
import { getProducts } from "./db";
import { FavoriteButton } from "./FavoriteButton"; // Client component
export default async function ProductCatalog() {
const products = await getProducts(); // Direct server fetch!
return (
<div className="grid grid-cols-3 gap-4">
{products.map((product: any) => (
<div key={product.id} className="border p-4 rounded">
<h4>{product.name}</h4>
<p>${product.price}</p>
<FavoriteButton productId={product.id} />
</div>
))}
</div>
);
}
<Suspense> to stream the rest of the HTML instantly while displaying a fallback loading state for the slow component.import { Suspense } from "react";
import ProductCatalog from "./ProductCatalog";
import LoadingSkeleton from "./LoadingSkeleton";
export default function Page() {
return (
<main className="p-8">
<h1>Store Dashboard</h1>
{/* Stream the product catalog asynchronously */}
<Suspense fallback={<LoadingSkeleton />}>
<ProductCatalog />
</Suspense>
</main>
);
}
Server Components Checklist
Marking it complete updates your roadmap progress percentage.