# React Server Components ## Server Components (Default) ```tsx // app/page.tsx - Server Component by default import { db } from '@/lib/db' export default async function Page() { // Data fetching in Server Component const users = await db.user.findMany() return (

Users

) } ``` ## Benefits of Server Components - **Zero bundle size** - Server Components don't add JavaScript to client bundle - **Direct backend access** - Query databases, read files, use secrets - **Automatic code splitting** - Only Client Components add to bundle - **Streaming** - Send UI progressively as data loads - **No client-side waterfalls** - Fetch all data in parallel on server ## Client Components ```tsx // components/counter.tsx 'use client' // Required directive import { useState } from 'react' export function Counter() { const [count, setCount] = useState(0) return ( ) } ``` ## When to Use Client Components Use `'use client'` when you need: - **Interactivity** - onClick, onChange, event handlers - **State** - useState, useReducer - **Effects** - useEffect, useLayoutEffect - **Browser APIs** - localStorage, window, document - **Custom hooks** - Any hook using client-only features - **Class components** - Component lifecycle methods ## Composition Pattern ```tsx // app/page.tsx - Server Component import { ClientWrapper } from './client-wrapper' import { db } from '@/lib/db' export default async function Page() { const data = await db.query() return (
{/* Server Component content */}

Server Content

{/* Pass data to Client Component */} {/* Server Component as children */}
) } // components/client-wrapper.tsx 'use client' export function ClientWrapper({ children, initialData, }: { children: React.ReactNode initialData: Data }) { const [data, setData] = useState(initialData) return (
{/* Client Component UI */} {/* Server Component children */} {children}
) } ``` ## Streaming with Suspense ```tsx // app/page.tsx import { Suspense } from 'react' import { SlowComponent } from './slow-component' import { FastComponent } from './fast-component' export default function Page() { return (
{/* Renders immediately */} {/* Shows fallback while loading */} Loading...
}> ) } // components/slow-component.tsx async function getData() { await new Promise(resolve => setTimeout(resolve, 3000)) return { data: 'Loaded!' } } export async function SlowComponent() { const data = await getData() return
{data.data}
} ``` ## Parallel Data Fetching ```tsx // app/dashboard/page.tsx async function getUser() { return fetch('https://api.example.com/user') } async function getPosts() { return fetch('https://api.example.com/posts') } export default async function Dashboard() { // Fetch in parallel const [user, posts] = await Promise.all([ getUser(), getPosts(), ]) return (
) } ``` ## Sequential Data Fetching ```tsx // app/artist/[id]/page.tsx async function getArtist(id: string) { return fetch(`https://api.example.com/artists/${id}`) } async function getAlbums(artistId: string) { return fetch(`https://api.example.com/artists/${artistId}/albums`) } export default async function ArtistPage({ params }: { params: { id: string } }) { // Sequential: albums depends on artist const artist = await getArtist(params.id) const albums = await getAlbums(artist.id) return (

{artist.name}

) } ``` ## Preloading Data ```tsx // lib/data.ts import { cache } from 'react' export const getUser = cache(async (id: string) => { return db.user.findUnique({ where: { id } }) }) // components/user-profile.tsx export async function UserProfile({ userId }: { userId: string }) { const user = await getUser(userId) return
{user.name}
} // app/page.tsx import { getUser } from '@/lib/data' import { UserProfile } from '@/components/user-profile' export default async function Page() { // Preload getUser('123') return (
{/* This will use cached result */}
) } ``` ## Server Component Patterns ### Pattern: Layout with Data Fetching ```tsx // app/dashboard/layout.tsx import { auth } from '@/lib/auth' import { db } from '@/lib/db' export default async function DashboardLayout({ children, }: { children: React.ReactNode }) { const session = await auth() const user = await db.user.findUnique({ where: { id: session.userId } }) return (
{children}
) } ``` ### Pattern: Conditional Client Components ```tsx // app/page.tsx import { ClientComponent } from './client-component' export default async function Page() { const data = await fetchData() // Only render Client Component when needed if (data.requiresInteractivity) { return } return
{data.content}
} ``` ### Pattern: Server Component with Client Island ```tsx // app/blog/[slug]/page.tsx import { LikeButton } from './like-button' export default async function BlogPost({ params }: { params: { slug: string } }) { const post = await getPost(params.slug) return (
{/* Server-rendered content */}

{post.title}

{/* Client island for interactivity */}
) } ``` ## Context in Server/Client Components ```tsx // app/providers.tsx 'use client' import { ThemeProvider } from 'next-themes' export function Providers({ children }: { children: React.ReactNode }) { return {children} } // app/layout.tsx import { Providers } from './providers' export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( {children} ) } ``` ## Third-Party Components ```tsx // components/carousel-wrapper.tsx 'use client' import { Carousel } from 'third-party-carousel' export function CarouselWrapper({ items }: { items: Item[] }) { return } // app/page.tsx import { CarouselWrapper } from '@/components/carousel-wrapper' export default async function Page() { const items = await fetchItems() return } ``` ## Edge Runtime ```tsx // app/api/route.ts export const runtime = 'edge' export async function GET() { return new Response('Hello from Edge!') } // app/page.tsx export const runtime = 'edge' export default async function Page() { return
Edge-rendered page
} ``` ## Quick Reference | Capability | Server Component | Client Component | |------------|------------------|------------------| | Data fetching | ✅ Yes | ⚠️ Use SWR/React Query | | Backend access | ✅ Yes (DB, files) | ❌ No | | Event handlers | ❌ No | ✅ Yes | | State/Effects | ❌ No | ✅ Yes | | Browser APIs | ❌ No | ✅ Yes | | Bundle size | 0 KB | Adds to bundle | | Streaming | ✅ Yes | ❌ No | ## Best Practices 1. **Default to Server Components** - Only use 'use client' when needed 2. **Move Client Components down** - Push them to leaves of component tree 3. **Pass data down** - Fetch in Server Components, pass to Client Components 4. **Use composition** - Nest Server Components inside Client Components via children 5. **Cache expensive operations** - Use React cache() for deduplication