Fiber Architecture & Rendering Pipeline
📖 Concept
React Fiber is the complete rewrite of React's core algorithm (React 16+). It replaced the old synchronous, recursive reconciler with an incremental, interruptible rendering engine.
Why Fiber was created: The old reconciler (Stack) processed the entire component tree synchronously. For a tree with 10,000 nodes, rendering was a single blocking operation — the JS thread couldn't handle user input or animations until done. On mobile, this caused dropped frames and unresponsive UIs.
Fiber's key innovation — work can be:
- Paused — Stop rendering mid-tree to handle urgent work
- Resumed — Continue where it left off
- Abandoned — Discard incomplete work if priorities changed
- Reused — Skip unchanged subtrees
Fiber node structure (each component becomes a Fiber):
FiberNode {
type: ComponentType, // Function, Class, or host ('View', 'Text')
key: string | null,
child: Fiber | null, // First child
sibling: Fiber | null, // Next sibling
return: Fiber | null, // Parent
stateNode: Instance, // DOM node / Native view / Class instance
memoizedState: any, // Hooks linked list (for function components)
memoizedProps: Props, // Last rendered props
pendingProps: Props, // Incoming props (not yet committed)
effectTag: number, // What needs to happen (placement, update, deletion)
alternate: Fiber | null, // Double buffering — current ↔ work-in-progress
}
Two-phase rendering:
Phase 1: Render (Interruptible)
- Traverses the Fiber tree (depth-first)
- Calls component functions / render methods
- Computes diffs (what changed)
- Builds a list of "effects" (side effects to apply)
- CAN be interrupted and restarted
Phase 2: Commit (Synchronous, Not interruptible)
- Applies all effects to the native view hierarchy
- Runs
useLayoutEffectcallbacks (synchronous) - Schedules
useEffectcallbacks (asynchronous) - CANNOT be interrupted — ensures UI consistency
Priority levels (Lanes model in React 18+):
| Priority | Examples | Interruptibility |
|---|---|---|
| Sync | User input, focus | Cannot be interrupted |
| Discrete | Click events | High priority |
| Continuous | Scroll, hover | Can be interrupted by discrete |
| Default | Data fetching results | Can be interrupted |
| Transition | startTransition | Lowest — can be abandoned |
| Idle | Prefetching, analytics | Only when nothing else to do |
💻 Code Example
1// === FIBER IN PRACTICE ===23// Understanding how Fiber processes this tree:4function App() {5 return (6 <View> {/* Fiber 1: child→Fiber2, sibling→null */}7 <Header /> {/* Fiber 2: child→null, sibling→Fiber3 */}8 <Content> {/* Fiber 3: child→Fiber4, sibling→Fiber5 */}9 <Article /> {/* Fiber 4: child→null, sibling→null */}10 </Content>11 <Footer /> {/* Fiber 5: child→null, sibling→null */}12 </View>13 );14}1516// Traversal order (depth-first):17// 1. View → 2. Header → 3. Content → 4. Article →18// (back up) 5. Footer → (back up to View, done)1920// Each step is a "unit of work" that CAN be interrupted between steps2122// === DOUBLE BUFFERING (current vs work-in-progress) ===2324// React maintains TWO fiber trees:25// - current: what's on screen right now26// - workInProgress: what we're building for the next update2728// After commit, workInProgress BECOMES current29// This enables:30// 1. Comparing old vs new (diffing)31// 2. Abandoning incomplete work without affecting displayed UI32// 3. Reusing unchanged fibers (structural sharing)3334// === PRACTICAL IMPLICATIONS FOR RN PERFORMANCE ===3536// 1. startTransition — mark updates as non-urgent37import { startTransition, useState } from 'react';3839function SearchScreen() {40 const [query, setQuery] = useState('');41 const [results, setResults] = useState([]);4243 const handleSearch = (text: string) => {44 // This is urgent — update input immediately45 setQuery(text);4647 // This is NOT urgent — can be interrupted by typing48 startTransition(() => {49 const filtered = expensiveFilter(allItems, text);50 setResults(filtered);51 });52 };5354 return (55 <>56 <TextInput value={query} onChangeText={handleSearch} />57 {/* Results render with lower priority — typing stays responsive */}58 <FlatList data={results} renderItem={renderItem} />59 </>60 );61}6263// 2. useDeferredValue — defer costly re-renders64import { useDeferredValue } from 'react';6566function SearchResults({ query }: { query: string }) {67 // deferredQuery updates with lower priority68 const deferredQuery = useDeferredValue(query);69 const isStale = query !== deferredQuery;7071 // Expensive computation uses deferred value72 const results = useMemo(73 () => filterItems(allItems, deferredQuery),74 [deferredQuery]75 );7677 return (78 <View style={{ opacity: isStale ? 0.7 : 1 }}>79 <FlatList data={results} renderItem={renderItem} />80 </View>81 );82}8384// 3. Understanding effect timing in Fiber85function EffectTimingDemo() {86 useEffect(() => {87 // Runs AFTER commit, asynchronously88 // Safe for: data fetching, subscriptions, analytics89 console.log('useEffect — async after paint');90 });9192 useLayoutEffect(() => {93 // Runs AFTER commit, SYNCHRONOUSLY, BEFORE browser paint94 // Use for: measuring layout, synchronous DOM updates95 // ⚠️ Blocks painting — keep it fast!96 console.log('useLayoutEffect — sync before paint');97 });9899 // Execution order:100 // 1. Render phase: component function runs101 // 2. Commit phase: DOM/native updates applied102 // 3. useLayoutEffect fires (sync, before paint)103 // 4. Browser paints104 // 5. useEffect fires (async, after paint)105}
🏋️ Practice Exercise
Fiber Architecture Exercises:
- Draw the Fiber tree (child/sibling/return pointers) for a component with 3 levels of nesting
- Use React DevTools Profiler to visualize which fibers are "committed" vs "bailed out" during a state update
- Implement a search input that uses
startTransitionto keep typing responsive while filtering 10K items - Compare the performance of
useEffectvsuseLayoutEffectfor measuring component dimensions - Create a demo showing how Fiber interrupts low-priority work when high-priority input arrives
⚠️ Common Mistakes
Putting expensive computations in the render function — they run in the render phase which may execute multiple times in concurrent mode
Using useLayoutEffect for async operations — it blocks painting and should only be used for synchronous layout measurements
Not understanding that render phase is NOT the same as commit phase — side effects in render can execute multiple times or never
Assuming startTransition makes things faster — it prioritizes responsiveness over throughput, the work still needs to be done
Not considering that React may call your component function multiple times during concurrent rendering — side effects MUST be in effects, not in the render body
💼 Interview Questions
🎤 Mock Interview
Mock interview is powered by AI for Fiber Architecture & Rendering Pipeline. Login to unlock this feature.