React Server Components (RSC) are components that run exclusively on the server and send their rendered HTML to the client. They cannot use client-side features like state, effects, or browser APIs — but they can directly access databases, file systems, and other server resources.
Key differences from Client Components: | Feature | Server Component | Client Component | |---------|------------------|------------------| | Runs on | Server | Client (browser) | | Access to | Database, filesystem, secrets | Browser APIs, state, effects | | JavaScript sent to client | No | Yes | | useState / useEffect | Cannot use | Can use | | Event handlers | Cannot use | Can use | | Direct DB queries | ✅ | ❌ |
How they work in Next.js App Router:
In Next.js 13+, components in app/ directory are Server Components by default. To make a component a Client Component, add "use client" at the top of the file.
Complete code example:
1// ============================================2// Server Component (default in Next.js App Router)3// ============================================4import { db } from "@/lib/database";5import AddToCartButton from "./AddToCartButton"; // client component67async function ProductList() {8 // Direct database access — no API route needed!9 const products = await db.query("SELECT * FROM products ORDER BY name");1011 return (12 <div>13 <h1>Products ({products.length})</h1>14 <ul>15 {products.map(product => (16 <li key={product.id}>17 <h2>{product.name}</h2>18 <p>{product.description}</p>19 <span>${product.price}</span>20 <AddToCartButton productId={product.id} />21 </li>22 ))}23 </ul>24 </div>25 );26}2728export default ProductList;2930// ============================================31// Client Component ("use client" directive)32// ============================================33"use client";34import { useState } from "react";3536function AddToCartButton({ productId }: { productId: string }) {37 const [loading, setLoading] = useState(false);38 const [added, setAdded] = useState(false);3940 const handleClick = async () => {41 setLoading(true);42 await fetch("/api/cart", {43 method: "POST",44 body: JSON.stringify({ productId }),45 });46 setAdded(true);47 setLoading(false);48 };4950 return (51 <button onClick={handleClick} disabled={loading}>52 {loading ? "Adding..." : added ? "Added!" : "Add to Cart"}53 </button>54 );55}5657export default AddToCartButton;
Benefits:
Critical pitfalls:
"use client" directiveWhen to use Server Components vs. Client Components: