React Server Components (RSCs)

A paradigm shift where components run exclusively on the server, never shipping JavaScript to the browser.

React6 min readConcept 46 of 48

What are Server Components?

Historically, all React components were **Client Components**. Even if they were initially rendered on the server (SSR), their code was eventually sent to the browser to be hydrated and executed.

**React Server Components (RSCs)** are a new type of component that only runs on the server. They resolve their data and return a special UI format (not HTML, but a serialized React tree). Their JavaScript code is *never* sent to the client.

Zero-Bundle Size and Direct DB Access

Because RSC code never reaches the browser, you can import massive backend libraries (like a markdown parser or a database ORM) without increasing the user's bundle size by a single byte.

Furthermore, because they run securely on the server, you can write async components that talk directly to your database without needing to create a middleman API route: const users = await db.query('SELECT * FROM users').

Mixing Server and Client Components

Server Components are fantastic for data fetching and heavy lifting, but they have a massive limitation: **they cannot have state or interactivity**. You cannot use useState, useEffect, or onClick in a Server Component.

To build interactive apps, you interleave them. You use Server Components for the layout and data-fetching shell, and you import smaller, targeted **Client Components** (marked with the 'use client' directive) for the specific parts of the UI that need interactivity (like a button or a form).

The Bundle Size Paradigm

Compare a traditional Client Component (fetching data) with a modern Server Component. Notice what happens to the JavaScript bundle size when importing heavy backend dependencies.

// Traditional Client Component
// Heavy dependency shipped to browser
'use client';
import { marked } from 'marked'; (>100kb!)
export function MarkdownViewer() {
const [html, setHtml] = useState();
useEffect(() => {
fetch('/api/post')
.then(res => res.text())
.then(md => setHtml(marked(md)));
}, []);
if (!html) return <Spinner/>;
return <divdangerouslySetInnerHTML={{__html: html}} />;
}
// React Server Component (RSC)
// Zero JS shipped. Direct DB access.
import { marked } from 'marked'; (Stays on server)
import db from '@/db';
export async function MarkdownViewer() {
// Await DB directly! No API route needed.
const post = await db.query('SELECT content FROM posts');
// Run heavy library on server
const html = marked(post.content);
return <divdangerouslySetInnerHTML={{__html: html}} />;
}
Server
Node.js
120kb JS(React + marked)
API JSON
2kb UI Payload0kb JS Shipped
Browser
Rendered HTML!The markdown parser ran successfully.

RSCs change the paradigm. By moving execution to the server, you drop heavy dependencies from the user's bundle and eliminate complex state management for data fetching.

Check yourself

Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.

  1. 1How much JavaScript is added to the browser's bundle when you use a React Server Component?
  2. 2Which of the following can you NOT use inside a React Server Component?
  3. 3How do you tell React that a component should be a Client Component (able to use state) in modern frameworks like Next.js App Router?

Remember this

  • Server Components run exclusively on the server and never ship JavaScript to the browser.
  • You can securely use direct database connections and secret API keys inside Server Components.
  • Server Components cannot use state, effects, or DOM event listeners.
  • Client Components (marked with 'use client') handle interactivity and state.
  • Pass data from Server Components to Client Components via props (must be serializable).

Done with this concept?

Mark it complete to track your progress. No login needed.