Loading...
Loading...
By continuing to use the platform, you accept the terms of the Privacy Policy and the use of cookies.
React Server Components (RSC) are components that execute exclusively on the server. Their code never ends up in the JavaScript bundle that the browser downloads. In the Next.js App Router (starting from version 13) all components are server components by default.
Before RSC all React code was sent to the browser. Even if a component simply displayed data from a database, its code, dependencies and logic were included in the client bundle.
Server Components solve three problems:
Bundle size. Server component code and their dependencies are not sent to the client. If you use a heavy data processing library, it stays on the server.
Direct data access. Server components can directly access the database, file system, environment variables. No intermediate API layer needed.
Security. Secrets (API keys, database connection strings) never reach the client.
// Server component (default)
// Runs on the server, code is not included in the bundle
import { db } from '@/lib/db'
export default async function ProblemsCount() {
const count = await db.problem.count()
return <p>Problems on Hack Frontend: {count}</p>
}
// Client component
// Marked with the 'use client' directive
'use client'
import { useState } from 'react'
export default function LikeButton() {
const [liked, setLiked] = useState(false)
return (
<button onClick={() => setLiked(!liked)}>
{liked ? 'Unlike' : 'Like'}
</button>
)
}
| Task | Server | Client |
|---|---|---|
| Fetching data from DB/API | ✓ | |
| Accessing env variables, files | ✓ | |
| Heavy dependencies (markdown parser, etc.) | ✓ | |
| useState, useEffect, useRef | ✓ | |
| onClick, onChange, onSubmit | ✓ | |
| Browser API (localStorage, geolocation) | ✓ | |
| React Context (useContext) | ✓ |
Next.js executes server components and creates a special format (RSC Payload) that describes the rendering result.
The RSC Payload is sent to the browser along with HTML. Client components are hydrated, server components are already rendered.
On navigation Next.js requests the RSC Payload for the new route. Server components re-render on the server, client components keep their state.
The most common pattern: a server component loads data and passes it to a client component via props:
// app/problems/page.tsx (server)
import { db } from '@/lib/db'
import { ProblemList } from './problem-list'
export default async function ProblemsPage() {
const problems = await db.problem.findMany({
orderBy: { difficulty: 'asc' }
})
return <ProblemList problems={problems} />
}
// app/problems/problem-list.tsx (client)
'use client'
import { useState } from 'react'
export function ProblemList({ problems }) {
const [filter, setFilter] = useState('all')
const filtered = filter === 'all'
? problems
: problems.filter(p => p.difficulty === Number(filter))
return (
<div>
<select onChange={e => setFilter(e.target.value)}>
<option value="all">All</option>
<option value="1">Easy</option>
<option value="2">Medium</option>
<option value="3">Hard</option>
</select>
<ul>
{filtered.map(p => (
<li key={p.id}>{p.name}</li>
))}
</ul>
</div>
)
}
A client component can receive a server component via children. This lets you create interactive wrappers without losing the benefits of server rendering:
// Client wrapper component
'use client'
import { useState } from 'react'
export function Accordion({ title, children }) {
const [open, setOpen] = useState(false)
return (
<div>
<button onClick={() => setOpen(!open)}>{title}</button>
{open && children}
</div>
)
}
// Server component uses the client wrapper
import { Accordion } from './accordion'
import { db } from '@/lib/db'
export default async function FAQ() {
const items = await db.faq.findMany()
return (
<div>
{items.map(item => (
<Accordion key={item.id} title={item.question}>
<p>{item.answer}</p>
</Accordion>
))}
</div>
)
}
Important:
You cannot import a server component inside a client component. If you add 'use client' to a file, all its imports become client-side too. Pass server components only via props or children.
Interview tip:
A common interview mistake: confusing Server Components with SSR. SSR renders the entire React tree on the server once and sends HTML. Server Components are a different model: part of the tree lives on the server permanently and never sends its code to the client. On navigation, server components re-render on the server.