Server Component — Direct DB Access, 0KB Client Bundle
Server Component — Direct DB Access, 0KB Client Bundle
💡 Why Should You Learn This? — 'Finish on the Server and Send Only HTML' Is the Default
Server vs Client — Boundaries and Rules
1. Server Component Is the DefaultIf there is no 'use client' at the top of a file, it is automatically a Server Component.``tsx// app/users/page.tsx — no 'use client' → Serverexport default async function UsersPage() { const users = await db.user.findMany(); // Direct DB access OK return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;}`## 2. Capabilities of Server Components| Feature | Server | Client ||---|---|---|| async function component | ✅ | ❌ || Direct await fetch() | ✅ | △ (only inside useEffect) || DB, file system, secret access | ✅ | ❌ || useState / useEffect | ❌ | ✅ || onClick / onChange | ❌ | ✅ || Browser APIs (window, localStorage) | ❌ | ✅ || Context Provider | △ | ✅ |## 3. Automatic fetch Deduplication + CachingEven if multiple components call the same URL within the same render, the actual fetch only happens once:`tsxasync function Header() { const user = await fetch('/api/me').then(r => r.json()); // First call return <div>{user.name}</div>;}async function Sidebar() { const user = await fetch('/api/me').then(r => r.json()); // Same fetch — automatic dedupe return <div>{user.email}</div>;}`Caching policies:`tsxfetch(url); // Default SSG (force-cache)fetch(url, { cache: 'no-store' }); // Fresh every timefetch(url, { next: { revalidate: 60 } });// 60-second ISRfetch(url, { next: { tags: ['user'] } });// Invalidate with revalidateTag('user')`## 4. Server → Client Boundary`tsx// app/page.tsx (Server) — Can import and embed Client componentsimport { Counter } from './Counter';export default async function Home() { const users = await db.user.findMany(); return ( <div> <h1>Users: {users.length}</h1> <Counter /> {/ Client Component /} </div> );}````tsx// app/Counter.tsx'use client';import { useState } from 'react';export function Counter() { const [n, setN] = useState(0); return <button onClick={() => setN(n + 1)}>{n}</button>;}`- A Server Component importing and embedding a Client Component is OK.- A Client Component importing and embedding a Server Component is ❌ (you can't call a DB from the browser).- However, passing a Server Component as children` into a Client Component is OK (composition).
💡 💡 5 Server Component Tips for Production
1. Only Server Components Can Be asyncAdding async to a Client Component causes a build error. Fetch data on the Server and pass the result down to the Client as props.2. 'use client' Does Not Spread UpwardEven if a Server imports a Client, the parent remains a Server. The boundary is clear.3. Client → Server Import Is Not AllowedIf a Client Component imports a Server Component and embeds it directly, you will get a build error. Use children instead:``tsx// ✅ composition pattern<ClientLayout><ServerContent /></ClientLayout>// ClientLayout only receives children as props`4. Four fetch Cache Modes`tsxfetch(url); // force-cache (default SSG)fetch(url, { cache: 'no-store' }); // SSR (fresh every time)fetch(url, { next: { revalidate: 60 } }); // ISR (60 seconds)fetch(url, { next: { tags: ['user'] } }); // Tag invalidation`5. Environment Variables — Server Sees Everything, Client Only Sees NEXT_PUBLIC_`tsprocess.env.DATABASE_URL // Server: OK, Client: undefinedprocess.env.NEXT_PUBLIC_API_URL // Both OK``Secrets embedded in a Server Component never leak to the browser.