# Next.js Server Components React Server Components (RSC) architecture, patterns, and best practices. ## Core Concepts ### Server Components (Default) All components in `app/` directory are Server Components by default: ```tsx // app/posts/page.tsx - Server Component async function getPosts() { const res = await fetch('https://api.example.com/posts') return res.json() } export default async function PostsPage() { const posts = await getPosts() return (
{posts.map(post => (
{post.title}
))}
) } ``` **Benefits:** - Fetch data on server (direct database access) - Keep sensitive data/keys on server - Reduce client-side JavaScript bundle - Improve initial page load and SEO - Cache results on server - Stream content to client **Limitations:** - Cannot use React hooks (useState, useEffect, useContext) - Cannot use browser APIs (window, localStorage) - Cannot add event listeners (onClick, onChange) - Cannot use React class components ### Client Components Mark with `'use client'` directive at top of file: ```tsx // components/counter.tsx - Client Component 'use client' import { useState } from 'react' export function Counter() { const [count, setCount] = useState(0) return ( ) } ``` **Use Client Components for:** - Interactive UI (event handlers) - State management (useState, useReducer) - Effects (useEffect, useLayoutEffect) - Browser-only APIs (localStorage, geolocation) - Custom React hooks - Context consumers ## Composition Patterns ### Server Component as Wrapper Best practice: Keep Server Components as parent, pass Client Components as children: ```tsx // app/page.tsx - Server Component import { ClientSidebar } from './sidebar' import { ClientButton } from './button' export default async function Page() { const data = await fetchData() // Server-side data fetch return (

Server-rendered heading

More server-rendered content: {data.title}

) } ``` ### Passing Server Components to Client Components Use children pattern to avoid making entire tree client-side: ```tsx // app/page.tsx - Server Component import { ClientProvider } from './client-provider' import { ServerContent } from './server-content' export default function Page() { return ( {/* Stays as Server Component */} ) } // client-provider.tsx - Client Component 'use client' export function ClientProvider({ children }: { children: React.ReactNode }) { const [state, setState] = useState() return
{children}
} // server-content.tsx - Server Component export async function ServerContent() { const data = await fetchData() return

{data.content}

} ``` ### Sharing Data Between Server Components No need for props or context - just fetch data where needed: ```tsx // lib/data.ts export async function getUser() { const res = await fetch('https://api.example.com/user', { cache: 'force-cache' // Will dedupe automatically }) return res.json() } // app/header.tsx import { getUser } from '@/lib/data' export async function Header() { const user = await getUser() // Fetch 1 return
Welcome, {user.name}
} // app/profile.tsx import { getUser } from '@/lib/data' export async function Profile() { const user = await getUser() // Fetch 2 (deduped automatically) return
Email: {user.email}
} ``` Next.js automatically dedupes identical fetch requests during render. ## Async Components Server Components can be async functions: ```tsx // app/posts/[id]/page.tsx async function getPost(id: string) { const res = await fetch(`https://api.example.com/posts/${id}`) return res.json() } async function getComments(postId: string) { const res = await fetch(`https://api.example.com/posts/${postId}/comments`) return res.json() } export default async function Post({ params }: { params: { id: string } }) { // Parallel data fetching const [post, comments] = await Promise.all([ getPost(params.id), getComments(params.id) ]) return (

{post.title}

{post.content}

) } ``` ## Streaming with Suspense Stream components as they resolve: ```tsx // app/page.tsx import { Suspense } from 'react' async function SlowComponent() { await new Promise(resolve => setTimeout(resolve, 3000)) return
Loaded after 3 seconds
} async function FastComponent() { await new Promise(resolve => setTimeout(resolve, 500)) return
Loaded after 0.5 seconds
} export default function Page() { return (

Instant heading

Loading fast...
}> Loading slow...}> ) } ``` Benefits: - Fast components render immediately - Slow components don't block page - Progressive enhancement - Better perceived performance ## Context in Server/Client Components ### Problem: Context Requires Client Components ```tsx // ❌ Won't work - Server Components can't use context import { createContext } from 'react' const ThemeContext = createContext() export default function Layout({ children }) { return ( {children} ) } ``` ### Solution: Create Client Component Wrapper ```tsx // app/providers.tsx - Client Component 'use client' import { createContext, useContext } from 'react' const ThemeContext = createContext('light') export function ThemeProvider({ children }: { children: React.ReactNode }) { return ( {children} ) } export function useTheme() { return useContext(ThemeContext) } // app/layout.tsx - Server Component import { ThemeProvider } from './providers' export default function RootLayout({ children }) { return ( {children} ) } ``` ## Third-Party Component Integration Many third-party components need client-side features: ```tsx // components/carousel.tsx 'use client' import 'slick-carousel/slick/slick.css' import Slider from 'react-slick' export function Carousel({ children }) { return {children} } // app/page.tsx - Server Component import { Carousel } from '@/components/carousel' export default function Page() { return (
Slide 1
Slide 2
) } ``` ## Server Actions Call server-side functions from Client Components: ```tsx // app/actions.ts 'use server' import { revalidatePath } from 'next/cache' import { db } from '@/lib/db' export async function createPost(formData: FormData) { const title = formData.get('title') as string const content = formData.get('content') as string await db.post.create({ data: { title, content } }) revalidatePath('/posts') } // app/new-post/page.tsx import { createPost } from '@/app/actions' export default function NewPost() { return (