React Server Components (RSC) changed how modern Next.js applications are built. By executing more logic on the server, apps ship less JavaScript to the browser while improving performance and simplifying data flow. In the App Router, components are Server Components by default.
Render and fetch on the server
A Server Component can await data directly — no useEffect, no loading boilerplate, no exposed API keys:
// app/posts/page.tsx — Server Component by default
async function PostsPage() {
const posts = await db.post.findMany(); // runs on the server
return (
<ul>
{posts.map((p) => <li key={p.id}>{p.title}</li>)}
</ul>
);
}
Database queries and secrets stay on the server, and the client never downloads the data-fetching code.
Use Client Components only when needed
Interactivity, browser APIs, and local state require a Client Component. Mark it explicitly and keep it small:
'use client';
import { useState } from 'react';
export function LikeButton() {
const [liked, setLiked] = useState(false);
return <button onClick={() => setLiked(!liked)}>{liked ? '♥' : '♡'}</button>;
}
The pattern: keep pages and layouts as Server Components, and push 'use client' down to the smallest interactive leaves. A server page can render a client button without becoming a client component itself.
Stream with Suspense
Instead of waiting for the whole page, Next.js can stream sections as they resolve:
import { Suspense } from 'react';
export default function Page() {
return (
<>
<Header />
<Suspense fallback={<p>Loading feed…</p>}>
<Feed /> {/* slow data — streams in when ready */}
</Suspense>
</>
);
}
Users see the shell and header immediately while the slow part fills in — better perceived performance without blocking the page.
Conclusion
Server Components are a core part of modern Next.js. The mental model is simple: server by default, client at the edges, stream the slow parts. Applied consistently, it produces faster, more secure, and more maintainable applications.