Understanding React Server Components From First Principles
Server Components aren't just a performance optimization, they're a different execution model. Here's the mental framework that makes them click.
By The Weekly Dev —
The core insight
React before Server Components had one execution environment: the browser. All components run there, all state lives there, all side effects happen there. The server rendered HTML for the initial load and then stepped aside.
Server Components add a second execution environment that never becomes the first. A Server Component renders on the server, produces output, and that's it. It never hydrates. It never re-renders. It has no lifecycle beyond the initial render.
Why this matters
When a component never runs in the browser, it can do things browser code can't: read from a database directly, access the filesystem, use secrets safely. The data access layer moves into the component tree.
// This runs only on the server, no API route needed
async function UserProfile({ id }: { id: string }) {
const user = await db.user.findUnique({ where: { id } });
return <div>{user.name}</div>;
}
No fetch, no loading state, no API to secure. The query runs at render time, on the server.
The boundary model
The React tree is now a mix of server and client components. The rules:
- Server Components can import Client Components
- Client Components cannot import Server Components (they can receive them as
childrenprops) - The boundary is where
"use client"appears
Think of it as a one-way door. Data flows down from server to client. Interactivity exists only on the client side of the boundary.
What changes about architecture
Data fetching moves closer to the components that need it. No more prop drilling from a page-level data fetcher to deeply nested components. Each component fetches its own data, and React deduplicates identical requests automatically.
The result is less code, fewer loading states, and clearer data ownership, once the mental model clicks.