React Server Components
Render components on the server to cut bundle size and fetch data directly
Overview
React Server Components (RSC) run only on the server and never ship their JavaScript to the browser. They can fetch data directly, access server resources, and keep secrets out of the client bundle. Interactive pieces opt into the client with the 'use client' directive; server and client components compose in the same tree.
Syntax / Usage
Server components are async functions that await data directly—no useEffect, no loading spinner plumbing.
// app/posts/page.tsx — a Server Component by default
async function PostsPage() {
const res = await fetch('https://api.example.com/posts', {
next: { revalidate: 60 },
})
const posts: Post[] = await res.json()
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
export default PostsPage
Examples
Mark interactive components with 'use client'; only these ship JS and may use hooks or event handlers:
'use client'
import { useState } from 'react'
export function LikeButton({ initial }: { initial: number }) {
const [likes, setLikes] = useState(initial)
return <button onClick={() => setLikes((n) => n + 1)}>{likes} ♥</button>
}
Compose them: a server component fetches data and passes serializable props to a client child:
async function Post({ id }: { id: string }) {
const post = await getPost(id)
return (
<article>
<h1>{post.title}</h1>
<LikeButton initial={post.likes} />
</article>
)
}
Common Mistakes
- Using hooks (
useState,useEffect) or browser APIs in a server component—those need'use client' - Passing non-serializable props (functions, class instances) from server to client components
- Importing a server-only module into a client component and leaking secrets to the bundle
- Assuming every component must be a client component; keep the default (server) where possible
- Forgetting that
'use client'marks a boundary—everything imported below it becomes client code
See Also
react-suspense use-effect components