react-core-architecture
Quick Reference
Architecture Layers
| Layer | Role | Key Concept |
|---|---|---|
| React Elements | Lightweight descriptions of UI | Immutable objects created by JSX/createElement |
| Components | Functions that return elements | Pure functions of props and state |
| Fiber Tree | Internal work-in-progress tree | Enables incremental rendering and prioritization |
| Reconciler | Diffing algorithm | Compares previous and next element trees |
| Renderer | Platform-specific output | react-dom for web, react-native for mobile |
Core Principles
| Principle | Rule |
|---|---|
| Unidirectional Data Flow | Data ALWAYS flows from parent to child via props |
| Declarative UI | ALWAYS describe what the UI should look like, NEVER imperatively mutate the DOM |
| Composition over Inheritance | ALWAYS compose components, NEVER use class inheritance for component reuse |
| Pure Rendering | The render phase MUST be a pure function of props and state |
| Immutable Updates | NEVER mutate state or props directly; ALWAYS create new references |
React Element vs Component
| Concept | What It Is | Example |
|---|---|---|
| React Element | Immutable plain object describing a DOM node or component | { type: 'div', props: { children: 'Hello' } } |
| Component | Function that accepts props and returns React elements | function Greeting({ name }: Props) { return <h1>{name}</h1>; } |
| Fiber | Internal mutable work unit tracking a component instance | Not directly accessible; managed by React internals |
Critical Warnings
NEVER mutate state or props during rendering -- rendering MUST be a pure calculation. Mutations cause inconsistent UI and break concurrent features.
NEVER rely on render timing or count -- React MAY call your component multiple times, skip renders, or pause and resume rendering. StrictMode double-invokes components in development.
NEVER perform side effects in the render phase (network requests, subscriptions, DOM mutations) -- ALWAYS use useEffect or event handlers for side effects.
NEVER use inheritance to share behavior between components -- ALWAYS use composition (children, render props, or custom hooks).
NEVER call root.render() where hydrateRoot() is needed -- for server-rendered HTML, ALWAYS use hydrateRoot to preserve server markup and attach event handlers.
NEVER assume synchronous DOM updates after root.render() -- rendering is asynchronous. Use flushSync() ONLY when synchronous behavior is explicitly required.
Rendering Model
JSX Compilation
JSX is syntactic sugar for React.createElement() calls:
// JSX (what you write)
<Greeting name="Taylor" />
// Compiled output (what React sees)
createElement(Greeting, { name: 'Taylor' })
The returned React element is an immutable object:
{
type: Greeting, // Component function or string tag
props: { name: 'Taylor' },
key: null,
ref: null
}
ALWAYS use capital letters for component names in JSX -- lowercase names resolve to HTML tags, not components.
Three-Phase Rendering Cycle
React updates the screen in three sequential steps:
| Phase | What Happens | Interruptible? |
|---|---|---|
| 1. Trigger | Initial root.render() call or a state update via setState |
N/A |
| 2. Render | React calls component functions and diffs the element tree | Yes (concurrent mode) |
| 3. Commit | React applies minimal DOM mutations to match the new tree | No (synchronous) |
After the commit phase, the browser paints the updated screen.
Render Phase (Pure)
- React calls your component function to produce a new element tree
- Compares the new tree with the previous tree (reconciliation)
- In concurrent mode (React 18+), this phase is interruptible -- React can pause, resume, or discard work
- MUST be pure: no side effects, no DOM mutations, no subscriptions
Commit Phase (Synchronous)
- React applies the minimal set of DOM changes identified during reconciliation
- Runs
useLayoutEffectcleanup and setup synchronously - The browser paints the screen
- Runs
useEffectcleanup and setup asynchronously after paint
Reconciliation Algorithm
React's diffing strategy uses two key heuristics:
- Different element types produce different trees -- React tears down the old subtree and builds a new one
- Keys identify which children remain stable across re-renders -- ALWAYS provide stable keys for list items
// React preserves <input> because the element type and position match
<div>
<input value={text} /> {/* Same position, same type = preserved */}
</div>
Fiber Architecture (React 16+)
The Fiber reconciler replaced the legacy stack reconciler to enable:
| Capability | Description |
|---|---|
| Incremental rendering | Split rendering work into chunks across multiple frames |
| Priority scheduling | Urgent updates (user input) preempt lower-priority work |
| Pause and resume | Interrupt in-progress work without losing progress |
| Concurrent rendering | Prepare multiple UI versions simultaneously (React 18+) |
Each fiber node represents a component instance and contains:
type-- the component function or host element tagstateNode-- the DOM node (for host elements) or component instancechild,sibling,return-- tree navigation pointersmemoizedState-- the linked list of hooks for this componentpendingProps,memoizedProps-- current and previous propslanes-- priority bits for scheduling (React 18+)
NEVER access fiber internals directly -- they are private implementation details that change between React versions.
Component Lifecycle
Function Component Lifecycle
Mount: Component called -> Elements created -> DOM inserted -> Effects run
Update: State/props change -> Component re-called -> Reconciliation -> DOM patched -> Effects re-run
Unmount: Effect cleanups run -> DOM removed
| Phase | What Runs | When |
|---|---|---|
| Mount | Component function, then useEffect callbacks |
First render, after DOM insertion |
| Update | Component function, then useEffect cleanups + callbacks (if deps changed) |
On state or props change |
| Unmount | useEffect cleanup functions |
When component is removed from tree |
Entry Point: createRoot
import { createRoot } from 'react-dom/client';
import { StrictMode } from 'react';
import App from './App';
const root = createRoot(document.getElementById('root')!, {
onCaughtError: (error, errorInfo) => {
console.error('Caught:', error, errorInfo.componentStack);
},
onUncaughtError: (error, errorInfo) => {
console.error('Uncaught:', error, errorInfo.componentStack);
},
});
root.render(
<StrictMode>
<App />
</StrictMode>
);
ALWAYS wrap the root in <StrictMode> during development to detect impure renders, missing effect cleanups, and deprecated APIs.
Component Tree Model
Render Tree
The render tree represents the component hierarchy for a single render pass:
- Nodes are React components (not HTML elements)
- Root node is the top-level component passed to
root.render() - Top-level components near the root affect performance of all descendants
- Leaf components at the bottom are frequently re-rendered
The tree changes dynamically with conditional rendering -- different state produces different subtrees.
Unidirectional Data Flow
State (parent) --> Props (child) --> Props (grandchild)
^ |
| |
+-------- Callbacks (events) <---------+
Data flows DOWN through props. Communication UP happens through callback functions passed as props. NEVER pass data upward by mutating parent state from a child without using a callback.
StrictMode Behavior (Development Only)
| Check | Method | Purpose |
|---|---|---|
| Impure rendering | Double-invokes component functions | Catches render-phase mutations |
| Missing effect cleanup | Runs setup -> cleanup -> setup cycle | Catches missing cleanup functions |
| Missing ref cleanup | Double ref callback cycle | Catches ref-related memory leaks |
| Deprecated APIs | Static warnings | Flags legacy lifecycle methods |
StrictMode checks run ONLY in development. They have zero impact on production builds.
React 18 vs React 19
| Feature | React 18 | React 19 |
|---|---|---|
| Concurrent rendering | Introduced via createRoot |
Stable, improved scheduling |
| Server Components | Experimental | Stable |
ref forwarding |
Requires forwardRef() |
ref is a regular prop |
| Context Provider | <MyContext.Provider value={}> |
<MyContext value={}> |
| Form handling | Manual state management | Built-in Actions pattern |
use() API |
Not available | Reads Promises and Context in render |
useActionState |
Not available | Manages async action state |
useOptimistic |
Not available | Optimistic UI updates |
| Metadata tags | Require react-helmet or similar |
Native <title>, <meta>, <link> hoisting |
| Error callbacks | onRecoverableError only |
onCaughtError, onUncaughtError, onRecoverableError |
Reference Links
- references/examples.md -- Working code examples for rendering, lifecycle, and tree structure
- references/api-table.md -- React core type reference and API signatures
- references/anti-patterns.md -- What NOT to do, with explanations