Skip to content
GeeksSmith
advanced 8 min read

React Fiber

Fiber is React's internal reconciliation architecture: a unit of work per component that lets rendering be split into chunks, paused, resumed, and prioritized instead of running as one uninterruptible pass.

Why it matters in interviews

Before Fiber (React 15 and earlier), reconciliation was a single recursive, synchronous walk of the tree that couldn't be interrupted — a big update could block the main thread and freeze input. Fiber is what makes concurrent features like transitions and Suspense possible.

Visual & Interactive Explanation

Interactive Flow Execution

Code Examples & Implementation

startTransition marks work as interruptible
import { startTransition, useState } from "react";

function Search() {
  const [query, setQuery] = useState("");
  const [results, setResults] = useState<string[]>([]);

  function handleChange(value: string) {
    setQuery(value); // urgent: keep the input responsive

    startTransition(() => {
      setResults(computeResults(value)); // low priority: can be interrupted
    });
  }

  return <input value={query} onChange={(e) => handleChange(e.target.value)} />;
}

Interview-Ready Answers

React's rendering engine — it represents each component as a unit of work so rendering can be paused, resumed, and prioritized instead of blocking the main thread.

Official Documentation & Specifications

Follow-up Questions & Deep Dives

🎯 Code Output — What Does This Print?

Question 1hard
function App() {
  console.log("render");
  
  useEffect(() => {
    console.log("effect (passive)");
  });

  useLayoutEffect(() => {
    console.log("layout effect");
  });

  return <div>Hello</div>;
}
// What is the exact order?
Question 2tricky
let renderCount = 0;

function ExpensiveList({ items }: { items: string[] }) {
  renderCount++;
  console.log("ExpensiveList render #" + renderCount);
  return <ul>{items.map((i) => <li key={i}>{i}</li>)}</ul>;
}

// In React 18 StrictMode development:
// <StrictMode><ExpensiveList items={["a"]} /></StrictMode>
// What logs on mount?

Common Mistakes & Anti-Patterns

  • Describing Fiber as 'just the virtual DOM' — it's the scheduling/reconciliation architecture, not the VDOM data structure itself
  • Assuming all updates are interruptible — the commit phase is not
  • Not connecting Fiber to why concurrent features (Suspense, transitions) became possible

Implementation Evolution: Anti-Pattern to Production

❌ Anti-Pattern: Side Effects inside Component Render Phase

// ❌ Dangerous Anti-Pattern: Mutating external variables or DOM inside render body
let renderCount = 0;

function UserProfile({ userId }: { userId: string }) {
  renderCount++; // Impure! Render phase can be paused, aborted, or restarted in Fiber
  document.title = `User ${userId}`; // Direct DOM mutation during render

  return <div>User #{userId}</div>;
}
Why this breaks: In Concurrent React / Fiber, the render phase is interruptible and can be executed multiple times before committing (or thrown away entirely). Side effects inside the render body lead to duplicated analytics, desynchronized DOM, and race conditions.

✅ Correct Pattern: Pure Render Body with Effects in Commit Phase

// ✅ Correct: Keep render body pure, execute side effects in useEffect / useLayoutEffect
function UserProfile({ userId }: { userId: string }) {
  useEffect(() => {
    document.title = `User ${userId}`;
  }, [userId]);

  return <div>User #{userId}</div>;
}
🚀

🚀 Production-Grade (Hardened): Concurrent startTransition with Deferred Filtering & Yielding

// 🚀 Production: Urgent input state split from non-urgent heavy background filter
import { useState, useTransition, useDeferredValue, useMemo } from "react";

export function FastSearchList({ allItems }: { allItems: string[] }) {
  const [query, setQuery] = useState("");
  const [isPending, startTransition] = useTransition();

  // Low-priority deferred filter
  const deferredQuery = useDeferredValue(query);

  const filtered = useMemo(() => {
    return allItems.filter((item) =>
      item.toLowerCase().includes(deferredQuery.toLowerCase())
    );
  }, [allItems, deferredQuery]);

  const handleInput = (val: string) => {
    // 1. High priority: Instant typing feedback
    setQuery(val);
  };

  return (
    <div>
      <input
        value={query}
        onChange={(e) => handleInput(e.target.value)}
        placeholder="Type to search..."
      />
      {isPending && <span className="spinner">Updating list...</span>}
      <ul style={{ opacity: isPending ? 0.7 : 1 }}>
        {filtered.slice(0, 100).map((item) => (
          <li key={item}>{item}</li>
        ))}
      </ul>
    </div>
  );
}

⚠️ Trick Questions & Interviewer Traps

Q1:Can React Fiber discard a half-completed render tree without painting it?

Q2:What is the Double Buffering technique in Fiber?

📋 Rapid Revision Cheat Sheet

  • Fiber Node: Unit of work with child, sibling, and return pointers (linked-list tree).
  • Double Buffering: 'current' tree on screen + 'workInProgress' tree in memory.
  • 2 Phases: Render phase (async, interruptible) vs Commit phase (sync, writes to DOM).
  • Time Slicing: Work loop yields to browser main thread to maintain 60/120 FPS.
  • Golden Rule: Never perform side effects in the render body; only in useEffect/lifecycle.

Real-World Architectural Scenario

A dashboard re-renders a 5,000-row table on every filter keystroke, and typing feels laggy. How does Fiber-era React help, and what would you actually change?

Rate Your Readiness

Rate how comfortably you can explain this in an interview to update your global readiness gauge:

Rate your confidence: