The Architect & The Builder: React’s Internal Split
Deconstructing React into two distinct phases—Render and Commit. Understand why this separation is the physics engine that makes Server Components possible.
Part of the series: The Senior Engineer's Guide to React Server Components
- Part 1The Architect & The Builder: React’s Internal Split
- Part 2The Wire & The Wall: Serialization & The Flight Protocol
- Part 3The Cost of Waking Up: Hydration & The Uncanny Valley
- Part 4The Great Split: Zero-Bundle-Size Architecture
- Part 5The Hole in the Donut: RSC Composition Patterns
- Part 6Breaking the Waterfall: Streaming & Suspense
- Part 7The Loop: Server Actions & The RPC Revival
- Part 8The Senior Playbook: Architecture, Patterns & Caching
Ask a Junior Developer what React does, and they will likely say: "It updates the DOM."
Ask a Senior Developer, and they might say: "It manages state and syncs it with the UI."
Both answers are functional, but they obscure the internal architecture that makes React Server Components (RSC) possible. To understand RSC, we have to stop looking at React as a single library and start seeing it as two distinct engines working in tandem.
We need to talk about The Architect and The Builder.
The History: Why React was Rewritten
In 2016, Sebastian Markbåge and the React team began a massive, risky rewrite of the React core, known as React Fiber.
On the surface, Fiber was about performance—specifically, the ability to pause and resume work so the browser wouldn't freeze during large updates. But structurally, it did something much more important:
It strictly enforced a separation between Calculating the Tree (The Render Phase) and Applying the Updates (The Commit Phase).
This separation is the "physics" that allows RSC to exist.
Phase 1: The Render Phase (The Architect)
Imagine an Architect working in a quiet, isolated office. They have blueprints, rulers, and math. They do not touch concrete. They do not hold hammers. Their job is pure calculation.
In React, this is the Render Phase.
When React calls your component function, it is acting as the Architect. It takes your data (State/Props) and calculates a description of what the UI should look like.
// The Architect's Signature
type RenderPhase = (props: Props, state: State) => ReactNode;The Rules of the Office
Because the Architect is just calculating, this phase must be Pure.
- Input: Props and State.
- Output: A JSON-like tree (React Elements).
- Side Effects: ❌ Forbidden. You cannot change the DOM, fetch data via
useEffect, or add event listeners here.
The Insight
Since this phase is just pure JavaScript calculation, it does not need a Browser.
It doesn't need window. It doesn't need document. It just needs a JavaScript runtime. This means the Architect can theoretically work anywhere—a Web Worker, a mobile device, or... a Node.js Server.
Phase 2: The Commit Phase (The Builder)
Once the Architect finishes the blueprints (The Virtual DOM), they hand them over to The Builder.
The Builder works on the construction site. It is loud, messy, and deals with physical reality.
In React, this is the Commit Phase.
This is where React takes the difference between the old blueprint and the new blueprint and applies it to the host environment.
The Rules of the Site
- Action:
appendChild,remove,setAttribute. - Side Effects: ✅ Allowed. This is where
useEffectruns. - Environment: Strictly The Browser. You cannot touch the DOM if there is no DOM.
Visualizing the Split
Let's look at a standard component and draw the line between the two phases.
export default function UserProfile({ name }: { name: string }) {
// --- PHASE 1: THE ARCHITECT (Render) ---
// This runs purely to calculate the output.
// This logic can run on a Server or a Client.
const formattedName = name.toUpperCase();
const isAdmin = name.startsWith('Admin');
// We return a "Blueprint" (React Element), not a DOM node.
return (
<div className='profile'>
<h1>{formattedName}</h1>
{/* --- PHASE 2: THE BUILDER (Commit) --- */}
{/*
The 'onClick' is an event listener.
It requires a browser environment to exist.
It is "attached" during the Commit phase.
*/}
<button onClick={() => alert('Hello')}>Greet</button>
{/*
'useEffect' is a strictly Post-Commit instruction.
It says: "After you build the wall, paint it blue."
*/}
<ClientSideEffects />
</div>
);
}The "Physics" of Server Components
Now, apply this mental model to React Server Components.
If we have two distinct jobs, why do we force the User's Browser to do both of them?
In a standard Client-Side App (CSR), the browser downloads the JavaScript, acts as the Architect (calculating the tree), and then acts as the Builder (painting the pixels). This is redundant.
RSC is simply outsourcing the Architect's job to the Server.
- Server (Architect): Runs the component logic. Connects to the DB. Generates the blueprint.
- Network: Sends the blueprint to the browser.
- Browser (Builder): Takes the blueprint and applies it to the DOM.
Why useEffect is Banned in RSC
You often hear: "You can't use hooks in Server Components."
Using our analogy, this becomes obvious. useEffect is an instruction for the Builder (runs after the commit). But Server Components only run the Architect phase.
The Server never "Commits." It never touches the DOM. Therefore, asking a Server Component to useEffect is like handing a wet paintbrush to an Architect who is miles away from the building. It’s physically impossible.
The Cost of Breaking the Rules
To be a Senior Engineer, you must be able to spot when these boundaries are violated.
When you put "Side Effects" (Builder logic) inside the "Render Phase" (Architect logic), you cause performance regressions and hydration errors.
The "Impure" Architect
export default function BadComponent() {
// ❌ VIOLATION: Reading from the "Site" inside the "Office"
// The Server has no window. This will crash during SSR/RSC.
const width = window.innerWidth;
return <div>Window is {width}</div>;
}The Blocking Architect
export default function SlowComponent() {
// ❌ VIOLATION: Heavy blocking work in the Render Phase.
// This freezes the Architect.
// If this runs on the Browser, the UI freezes.
// If this runs on the Server, the Response hangs.
const primes = calculatePrimes(10000000);
return <div>{primes}</div>;
}Summary
- Render Phase: Pure calculation. Environment agnostic. This is where Server Components live.
- Commit Phase: DOM Manipulation and Side Effects. Browser specific. This is where Client Components take over.
Understanding this split is the prerequisite for understanding everything else. In the next article, we will look at how the Architect sends the blueprints to the Builder without sending the actual code.
Challenge: The Phase Audit
Look at the following code snippet. It works, but it mixes concerns.
Task: Identify which lines belong to the Render Phase (Architect) and which belong to the Commit Phase (Builder).
import { useState, useEffect } from 'react';
export default function Dashboard({ data }: { data: number[] }) {
// Line A
const total = data.reduce((acc, n) => acc + n, 0);
// Line B
const [visible, setVisible] = useState(false);
// Line C
useEffect(() => {
document.title = `Total: ${total}`;
}, [total]);
// Line D
if (total === 0) {
return <p>No Data</p>;
}
// Line E
return (
<div style={{ opacity: visible ? 1 : 0 }}>
<h1>{total}</h1>
</div>
);
}Click to Reveal Solution
- Line A (Render): Pure calculation. Can run on Server.
- Line B (Render):
useStateinitialization happens during the render pass to determine the initial value. - Line C (Commit):
useEffectis strictly a side effect. It runs after the DOM is updated. This cannot run on the Server. - Line D (Render): Conditional logic determining the output tree.
- Line E (Render): Returning the React Element blueprint.