What are Server Components?
React Server Components (RSC) are components that render exclusively on the server. Unlike Client Components, they don't send JavaScript to the browser — only the resulting HTML.
By default, all components in the Next.js App Router are Server Components.
Server vs Client Components
| Aspect | Server Component | Client Component |
|---|---|---|
| Rendering | Server | Server + Client |
| JavaScript in browser | No | Yes |
Hooks (useState, etc.) | No | Yes |
| Event handlers | No | Yes |
| Data fetching | Direct (async) | Via useEffect or SWR |
When to use each
Server Components (default)
// app/posts/page.tsx — Server Component
async function PostsPage() {
const posts = await fetch('https://api.example.com/posts').then((r) => r.json())
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}Client Components
Add "use client" at the top when you need interactivity:
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>Clicks: {count}</button>
}Keep Client Components as small as possible — import them inside Server Components to minimize the JavaScript bundle.
Composition: the ideal pattern
The recommended strategy is to compose Server and Client Components:
// Server Component (page.tsx)
import { InteractiveChart } from './chart'
export default async function DashboardPage() {
const data = await getAnalytics()
return (
<div>
<h1>Dashboard</h1>
<InteractiveChart data={data} />
</div>
)
}The Server Component handles the heavy fetch; the Client Component manages only the chart interactivity.
Streaming and Suspense
Combine Server Components with Suspense for granular loading states:
import { Suspense } from 'react'
function PostSkeleton() {
return <div className="animate-pulse h-32 bg-gray-200 rounded" />
}
export default function Page() {
return (
<Suspense fallback={<PostSkeleton />}>
<SlowPostsList />
</Suspense>
)
}Conclusion
Server Components are a fundamental shift in how we build React apps. Less JavaScript on the client, better performance, and simplified data fetching — all while keeping the developer experience you already know.
useEffect(() => {\n fetch('/api/data')\n .then(r => r.json())\n .then(setData)\n}, [])const data = await fetch('/api/data')\n .then(r => r.json())