SSR & React Server Components
React Server Components (RSC) are the biggest architectural shift in React in years - the server renders components into a streamable format that the client can hydrate selectively.
RSC fundamentals
EXAMPLE
// 1. The mental model
// - Server components run on the server, can fetch data, but cannot use state/effects or browser APIs.
// - Client components run on the client (and during SSR), can use state/effects.
// - You compose: server components import client components freely; the reverse only via props that are serialisable.
// 2. Server component - default in app/ in Next 13+
// app/users/page.tsx
import { db } from '@/lib/db';
import { UserListClient } from './UserListClient';
export default async function UsersPage() {
const users = await db.users.findMany();
return <UserListClient users={users} />;
}
// 3. Client component - opt-in with directive
// app/users/UserListClient.tsx
'use client';
import { useState } from 'react';
type Props = { users: { id: string; name: string }[] };
export function UserListClient({ users }: Props) {
const [q, setQ] = useState('');
return (
<div>
<input value={q} onChange={(e) => setQ(e.target.value)} />
<ul>
{users.filter((u) => u.name.includes(q)).map((u) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
</div>
);
}
// 4. Server actions - call server code from client components
// app/actions.ts
'use server';
export async function createUser(formData: FormData) {
await db.users.create({ name: String(formData.get('name')) });
}
// app/users/Add.tsx
'use client';
import { createUser } from '../actions';
export function Add() {
return (
<form action={createUser}>
<input name='name' />
<button>Add</button>
</form>
);
}
// 5. Streaming with Suspense
import { Suspense } from 'react';
export default function Dashboard() {
return (
<div>
<h1>Stats</h1>
<Suspense fallback={<p>loading...</p>}>
<Stats /> {/* server component fetching data */}
</Suspense>
</div>
);
}
// 6. Things you cannot do in a server component
// - useState, useEffect, useRef
// - Browser APIs (window, document)
// - Event handlers in returned JSX (onClick on a button must be in a client component)
// 7. Things RSC unlocks
// - Big DB queries directly in the component, no API layer
// - Smaller JS bundle - client components are only the interactive bits
// - Streaming UI - render shell first, suspense the slow bits
// 8. State of the union (2026)
// - Next.js App Router: production-ready RSC
// - React Router v7 framework mode: server components opt-in
// - Most other frameworks: not yet, or experimental
Why it matters
RSC moves the data layer out of useEffect and into the component itself. The bundle shrinks; the queries co-locate with the JSX. Client components keep doing what they always did. Start with everything as a server component; opt into client only where you actually need state.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// React Server Components run only on the server — no client JS shipped.
async function Posts() { const data = await db.posts.findMany(); return <List items={data} />; }
Try it Yourself »
Discussion
Loading…