[{"data":1,"prerenderedAt":116},["ShallowReactive",2],{"qa-\u002Freact\u002Frendering-and-performance\u002Fvirtual-dom-reconciliation":3},{"page":4,"siblings":96,"blog":113},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":20,"order":21,"path":22,"questions":23,"questionsCount":86,"related":87,"seo":88,"seoDescription":89,"stem":90,"subtopic":91,"topic":92,"topicSlug":93,"updated":94,"__hash__":95},"qa\u002Freact\u002Frendering-and-performance\u002Fvirtual-dom-reconciliation.md","Virtual Dom Reconciliation",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","React","react",{"subtopicSlug":19},"virtual-dom-reconciliation",true,1,"\u002Freact\u002Frendering-and-performance\u002Fvirtual-dom-reconciliation",[24,29,33,37,41,45,49,53,58,62,66,70,74,78,82],{"id":25,"difficulty":26,"q":27,"a":28},"what-is-virtual-dom","easy","What is the virtual DOM and why does React use it?","The **virtual DOM** is an in-memory JavaScript object tree that mirrors the\nreal DOM structure. React keeps this lightweight copy and uses it as a\nscratchpad to figure out the minimum set of real DOM mutations needed to\nbring the UI up to date.\n\n```jsx\n\u002F\u002F After setState React builds a new VDOM tree like this in memory:\n{ type: 'ul', props: { className: 'list' }, children: [\n  { type: 'li', props: { key: 'a' }, children: ['Alpha'] },\n  { type: 'li', props: { key: 'b' }, children: ['Beta'] },\n]}\n\u002F\u002F Diffing against the previous tree finds only the changed nodes,\n\u002F\u002F then a single batched DOM update is issued.\n```\n\nDirect DOM manipulation is expensive because the browser must recalculate\nlayout, style, and paint. By diffing two cheap JS objects first, React\nminimises the number of DOM operations — especially important when many\nstate updates happen in quick succession.\n\n**Rule of thumb:** The virtual DOM is a performance optimisation, not a\nfundamental requirement of React — React Native and React Three Fiber use\nthe same reconciler with no DOM at all.\n",{"id":30,"difficulty":26,"q":31,"a":32},"reconciliation-definition","What is reconciliation?","**Reconciliation** is the process React uses to decide how to update the\nreal DOM (or host environment) when state or props change. It compares the\nnewly rendered element tree against the previous one and computes a minimal\ndiff.\n\nReact's reconciler applies two heuristics to keep the diff O(n) instead of\nO(n³):\n\n1. **Different type → tear down.** If the root element of a subtree changes\n   type (e.g., `\u003Cdiv>` → `\u003Cspan>`), React destroys the old subtree and\n   mounts a fresh one.\n2. **Same type → update in place.** If the type is the same, React updates\n   only the changed props and recurses into children.\n\n```jsx\n\u002F\u002F Same type → React patches props, does NOT unmount\n\u003CButton color=\"red\" \u002F>  \u002F\u002F prev\n\u003CButton color=\"blue\" \u002F> \u002F\u002F next  → only color prop updated\n\n\u002F\u002F Different type → full unmount + remount\n\u003Cdiv>Hello\u003C\u002Fdiv>  \u002F\u002F prev\n\u003Cspan>Hello\u003C\u002Fspan> \u002F\u002F next → div destroyed, span created\n```\n\n**Rule of thumb:** Reconciliation is cheap for most apps, but mismatched\nkeys or unstable component types (functions defined in render) can force\nunnecessary unmounts.\n",{"id":34,"difficulty":14,"q":35,"a":36},"fiber-architecture","What is React Fiber and how does it improve on the old stack reconciler?","**Fiber** (React 16+) is a rewrite of React's reconciler that replaces the\nold synchronous, recursive \"stack reconciler\" with an interruptible,\nincremental system.\n\nThe old reconciler walked the component tree in one synchronous call that\ncould not be paused — long trees blocked the main thread and dropped frames.\n\nFiber represents each unit of work as a lightweight **fiber node** (a plain\nJS object) linked in a tree. The reconciler can:\n\n- **Pause** work mid-tree and resume it later.\n- **Abort** lower-priority work when something more urgent arrives.\n- **Reuse** completed work across multiple renders.\n\n```\nFiber node (simplified):\n{\n  type,          \u002F\u002F component type or host string\n  stateNode,     \u002F\u002F real DOM node or class instance\n  child,         \u002F\u002F first child fiber\n  sibling,       \u002F\u002F next sibling fiber\n  return,        \u002F\u002F parent fiber\n  pendingProps,\n  memoizedProps,\n  memoizedState,\n  effectTag,     \u002F\u002F INSERT \u002F UPDATE \u002F DELETE\n}\n```\n\nThis architecture unlocks concurrent features like `startTransition`,\n`useDeferredValue`, and `Suspense` — things that are impossible without the\nability to pause and reprioritise work.\n\n**Rule of thumb:** You rarely interact with Fiber directly, but understanding\nit explains why concurrent React can keep the UI responsive while doing heavy\nrendering work.\n",{"id":38,"difficulty":14,"q":39,"a":40},"diffing-algorithm-keys","Why do lists need keys, and what happens when keys are missing or wrong?","When React reconciles a list of children it needs a way to match old items\nto new items across renders. Without a stable identity, it falls back to\n**index-based matching**, which breaks as soon as items are reordered,\ninserted, or removed.\n\n```jsx\n\u002F\u002F Bad: using index as key\n{items.map((item, i) => \u003CRow key={i} data={item} \u002F>)}\n\u002F\u002F If item at index 0 is deleted, all subsequent rows get new data\n\u002F\u002F but keep their existing DOM nodes and state — visual corruption.\n\n\u002F\u002F Good: stable domain ID\n{items.map(item => \u003CRow key={item.id} data={item} \u002F>)}\n\u002F\u002F React tracks each Row by id; deletion only unmounts one node.\n```\n\nWrong keys cause two classes of bugs:\n\n1. **State leaking across items** — a text input retains its value when the\n   item it belongs to changes.\n2. **Missed unmounts** — components that should destroy (and clean up\n   effects) are reused instead.\n\nKeys also force a fresh mount when you *want* to reset state — give a\ncomponent a different `key` and React treats it as a completely new element.\n\n**Rule of thumb:** Keys must be stable, unique among siblings, and derived\nfrom your data — not from array index unless the list is static and never\nreordered.\n",{"id":42,"difficulty":26,"q":43,"a":44},"render-trigger-conditions","What causes a React component to re-render?","A component re-renders when **React decides its output may have changed**.\nThe four triggers are:\n\n1. **State update** — `setState` \u002F setter from `useState` is called with a\n   value that is not referentially equal to the current one.\n2. **Parent re-renders** — by default, when a parent renders, all its\n   children render too (unless bailed out by `React.memo`).\n3. **Context change** — any component subscribed to a context re-renders\n   when the context value changes.\n4. **`forceUpdate`** — class-component escape hatch, rarely used.\n\n```jsx\nfunction Child({ value }) {\n  \u002F\u002F Re-renders whenever Parent re-renders, even if value is the same,\n  \u002F\u002F unless wrapped in React.memo.\n  return \u003Cdiv>{value}\u003C\u002Fdiv>\n}\n```\n\nReact does **not** automatically bail out based on whether props are\nshallowly equal — that optimisation requires `React.memo` or\n`shouldComponentUpdate`.\n\n**Rule of thumb:** Unnecessary re-renders are usually harmless in small\ntrees; profile before optimising, as `React.memo` and memoisation hooks add\ntheir own overhead.\n",{"id":46,"difficulty":14,"q":47,"a":48},"commit-phase-vs-render-phase","What is the difference between the render phase and the commit phase?","React's update cycle has two distinct phases with very different guarantees.\n\n**Render phase** (pure, may be interrupted):\n- React calls your component function (or `render()`) to get a new element\n  tree.\n- Diffs the result against the previous tree.\n- Builds a list of effects to apply.\n- May be paused, aborted, or restarted in Concurrent Mode — so the render\n  phase must be **side-effect free**.\n\n**Commit phase** (synchronous, cannot be interrupted):\n- React applies DOM mutations from the effect list.\n- Runs `useLayoutEffect` callbacks (before the browser paints).\n- Lets the browser paint.\n- Runs `useEffect` callbacks (after the browser paints).\n\n```jsx\n\u002F\u002F Safe in render phase: pure calculation\nconst doubled = value * 2\n\n\u002F\u002F Safe only in commit phase: DOM mutation, network request\nuseEffect(() => {\n  document.title = `Count: ${count}` \u002F\u002F runs after commit\n}, [count])\n```\n\n**Rule of thumb:** Never put side effects directly in your component body —\nthey may run multiple times or be thrown away during concurrent rendering.\n",{"id":50,"difficulty":14,"q":51,"a":52},"reconciler-vs-renderer","What is the difference between the React reconciler and a renderer?","The **reconciler** (`react` package) is framework-agnostic: it builds and\ndiffs the virtual element tree, manages state and effects, and schedules\nwork. It knows nothing about how to actually display anything.\n\nA **renderer** is the bridge between the reconciler and a specific host\nenvironment. It receives instructions (create node, update prop, delete node)\nand executes them:\n\n| Renderer | Host environment |\n|---|---|\n| `react-dom` | Browser DOM |\n| `react-native` | iOS \u002F Android views |\n| `@react-three\u002Ffiber` | WebGL \u002F Three.js |\n| `react-test-renderer` | Plain JS objects (testing) |\n| `react-pdf` | PDF document |\n\n```jsx\n\u002F\u002F react (reconciler) figures out *what* changed\n\u002F\u002F react-dom (renderer) figures out *how* to apply it to the DOM\nimport { createRoot } from 'react-dom\u002Fclient'\ncreateRoot(document.getElementById('root')).render(\u003CApp \u002F>)\n```\n\n**Rule of thumb:** React's portability comes from this separation — you\ncan write the same component logic and target any host environment by\nswapping the renderer.\n",{"id":54,"difficulty":55,"q":56,"a":57},"bailout-conditions","hard","When does React bail out of re-rendering a subtree?","React skips re-rendering a subtree under two conditions:\n\n1. **Same element reference** — if `renderChildren()` returns the same\n   object (by reference, not value) as last time, React skips diffing that\n   subtree entirely. This is the basis of the `children` prop optimisation:\n   elements created in the parent and passed as props are created before the\n   parent re-renders, so their references remain stable.\n\n2. **`React.memo` \u002F `PureComponent` \u002F `shouldComponentUpdate`** — if the\n   component is wrapped and its props are shallowly equal, React skips the\n   render. `React.memo` with a custom comparator lets you define equality\n   yourself.\n\n```jsx\n\u002F\u002F Pattern 1: stable children reference\nfunction Parent() {\n  const [count, setCount] = useState(0)\n  return \u003CLayout>{\u002F* children created here don't re-create on Parent re-render *\u002F}\u003C\u002FLayout>\n}\n\n\u002F\u002F Pattern 2: React.memo\nconst Child = React.memo(function Child({ value }) {\n  return \u003Cdiv>{value}\u003C\u002Fdiv>\n})\n\u002F\u002F Child re-renders only when `value` changes (shallow equal check)\n```\n\nNote: the setter from `useState` and values from refs never change reference\nacross renders, so they never cause unnecessary re-renders in memoised\nchildren.\n\n**Rule of thumb:** Bail-out only skips the *render* call — effects and\ncontext subscriptions are still checked, and the fibre is still visited\nbriefly in the reconciler's tree walk.\n",{"id":59,"difficulty":14,"q":60,"a":61},"what-is-hydration","What is hydration and why can mismatches cause problems?","**Hydration** is the process by which React attaches event listeners and\ntakes over management of server-rendered HTML without re-creating the DOM\nfrom scratch.\n\nDuring hydration React walks the existing DOM and the virtual tree in\nparallel. If they match, React reuses the nodes (fast). If they don't match\n— a **hydration mismatch** — React either logs a warning and patches the DOM,\nor (in development) tears it down and re-renders from scratch.\n\n```jsx\n\u002F\u002F Common cause: rendering different content on server vs client\n\u003Cp>{typeof window !== 'undefined' ? 'Client' : 'Server'}\u003C\u002Fp>\n\u002F\u002F           ^--- different on server vs browser → mismatch warning\n```\n\nMismatches are problematic because:\n- Patches are extra work (doubles DOM writes on affected nodes).\n- Content flickers when the DOM is replaced.\n- Accessibility tools may read the pre-patch content.\n\nFixes: use `suppressHydrationWarning` for intentionally dynamic values\n(timestamps), defer client-only content with `useEffect` + client-only\n`useState`, or use `\u003CClientOnly>` wrapper patterns.\n\n**Rule of thumb:** Render the exact same tree on server and client; treat any\nhydration warning as a correctness bug, not a cosmetic issue.\n",{"id":63,"difficulty":14,"q":64,"a":65},"strict-mode-double-render","Why does React 18 render components twice in Strict Mode?","In development, React **StrictMode** intentionally calls your component\nfunction twice (then discards the first result) to help you discover\nside effects hidden in the render phase.\n\n```jsx\n\u002F\u002F React calls this TWICE in dev\u002FStrictMode:\nfunction Counter() {\n  console.log('render') \u002F\u002F you'll see this twice in the console\n  const [count, setCount] = useState(0)\n  return \u003Cbutton onClick={() => setCount(c => c + 1)}>{count}\u003C\u002Fbutton>\n}\n```\n\nIf your render function is truly pure (no side effects), double-invocation\nis invisible — you get the same result both times. But if render contains a\nside effect (incrementing a ref, logging an analytics event, mutating\nexternal state), you'll see it doubled, revealing the bug.\n\nThis matches the Concurrent Mode contract: the render phase may be discarded\nand restarted at any time, so it must always be side-effect free.\n\n**Rule of thumb:** Strict Mode double-invocation only happens in development\nand is one of the easiest ways to catch render-phase side effects before they\ncause production bugs.\n",{"id":67,"difficulty":55,"q":68,"a":69},"element-vs-component-vs-fiber","What is the difference between a React element, a component, and a fiber?","These three terms are often confused because they're all part of the same\npipeline.\n\n**React element** — a plain JS object describing what to render. Created\nby JSX (`\u003CButton \u002F>` → `React.createElement(Button, null)`). Cheap to\ncreate, immutable, thrown away after reconciliation.\n\n```js\n\u002F\u002F An element is just data:\n{ type: Button, props: { color: 'blue' }, key: null, ref: null }\n```\n\n**Component** — a function (or class) that accepts props and returns\nelements. It's the template; React calls it to produce elements.\n\n**Fiber** — a live node in React's internal work tree, one per element in\nthe rendered tree. Fibers are long-lived; they persist across renders and\ncarry state, effects, and work-scheduling metadata. They are *not* exposed\nto user code.\n\n```\nFlow:\nJSX → createElement → element (data)\n                           ↓ reconciler\n                        fiber (live node in Fiber tree, carries state)\n                           ↓ renderer\n                      real DOM node\n```\n\n**Rule of thumb:** You write components, you receive elements from JSX,\nand React manages fibers internally — mixing these terms up in an interview\nusually signals a shallow understanding of React internals.\n",{"id":71,"difficulty":55,"q":72,"a":73},"concurrent-rendering-basics","What is concurrent rendering and what problem does it solve?","**Concurrent rendering** (React 18+) lets React prepare multiple versions\nof the UI at the same time and interrupt, pause, or abandon in-progress\nrenders when higher-priority work arrives.\n\nThe problem it solves: in synchronous React, any state update — even a\nlow-priority background one — could block the main thread, causing the UI\nto feel janky during large re-renders (typing into a filtered list,\nanimating while data loads).\n\n```jsx\nimport { startTransition } from 'react'\n\n\u002F\u002F Mark filter update as non-urgent — React can pause it\n\u002F\u002F if the user types again before it finishes\nstartTransition(() => {\n  setFilter(e.target.value)\n})\n```\n\nKey tools:\n- `startTransition` \u002F `useTransition` — mark an update as low priority.\n- `useDeferredValue` — defer a derived value to avoid blocking.\n- `Suspense` — declaratively show a fallback while async work is pending.\n\n**Rule of thumb:** Concurrent rendering is opt-in per update, not a\nmode you flip globally. Most updates are still synchronous; you use\n`startTransition` only for genuinely expensive, deferrable work.\n",{"id":75,"difficulty":26,"q":76,"a":77},"why-pure-render","Why must React component render functions be pure?","A **pure function** always produces the same output for the same inputs and\nhas no side effects. React requires render functions to be pure because:\n\n1. **The render phase can be interrupted and restarted** (Concurrent Mode)\n   — side effects in render would fire multiple times or be abandoned\n   mid-execution.\n2. **Strict Mode double-invokes renders** — to detect purity violations in\n   development.\n3. **Server-side rendering** — the render phase runs in Node.js where no\n   DOM, `window`, or browser APIs exist; impure renders that touch these\n   break SSR.\n\n```jsx\n\u002F\u002F Impure render — BAD\nlet calls = 0\nfunction Counter() {\n  calls++          \u002F\u002F side effect: mutates external state on every render\n  return \u003Cp>{calls}\u003C\u002Fp>\n}\n\n\u002F\u002F Pure render — GOOD\nfunction Counter({ count }) {\n  return \u003Cp>{count}\u003C\u002Fp>  \u002F\u002F same props → same output, every time\n}\n```\n\n**Rule of thumb:** If you need to produce a side effect (fetch, log, update\nthe DOM), put it inside `useEffect` or an event handler — never directly in\nthe function body.\n",{"id":79,"difficulty":14,"q":80,"a":81},"passive-effects-layout-effects","What is the difference between useEffect and useLayoutEffect in terms of when they run in the commit phase?","Both hooks run *after* React commits changes to the DOM, but at different\npoints in the browser's rendering pipeline.\n\n**`useLayoutEffect`** fires **synchronously** after DOM mutations but\n**before** the browser paints. Use it when you need to read layout\ninformation (element sizes, scroll position) or make DOM mutations that\nmust be invisible to the user.\n\n**`useEffect`** fires **asynchronously** after the browser has painted. It\ndoesn't block the visual update, making it the right place for network\nrequests, subscriptions, and analytics.\n\n```\nReact commits DOM mutations\n      ↓\nuseLayoutEffect runs (synchronous, before paint)\n      ↓\nBrowser paints\n      ↓\nuseEffect runs (asynchronous, after paint)\n```\n\n```jsx\nuseLayoutEffect(() => {\n  \u002F\u002F Safe to read DOM measurements here — layout is complete but not painted\n  const height = ref.current.getBoundingClientRect().height\n  setHeight(height) \u002F\u002F won't cause visible flash\n})\n```\n\n**Rule of thumb:** Start with `useEffect`; switch to `useLayoutEffect` only\nif you see a visible flash caused by a DOM measurement + state update cycle.\n",{"id":83,"difficulty":14,"q":84,"a":85},"react-profiler","How do you identify performance bottlenecks in a React application?","The primary tool is the **React DevTools Profiler** tab, which records a\nflame chart of which components rendered, how long each took, and why each\nrendered (changed props, state, context, or parent).\n\nSteps:\n\n1. Open DevTools → Profiler → click Record.\n2. Perform the interaction that feels slow.\n3. Stop recording and inspect the flame chart.\n4. Look for components with long render times or high render counts.\n5. Check \"Why did this render?\" for specific cause.\n\n```jsx\n\u002F\u002F Instrument a component boundary for programmatic profiling\n\u003CReact.Profiler id=\"FilterPanel\" onRender={(id, phase, actualDuration) => {\n  console.log(id, phase, actualDuration)\n}}>\n  \u003CFilterPanel \u002F>\n\u003C\u002FReact.Profiler>\n```\n\nAlso useful:\n- Browser Performance tab for the full main-thread timeline.\n- `console.time` \u002F `console.timeEnd` around suspected slow code.\n- `why-did-you-render` library to log unexpected re-renders.\n\n**Rule of thumb:** Profile first, then optimise. Adding `React.memo` and\nmemoisation hooks everywhere without profiling often makes performance *worse*\nby increasing the cost of the equality checks themselves.\n",15,null,{"description":11},"React virtual DOM and reconciliation interview questions — diffing algorithm, Fiber architecture, keys, bailout conditions, and how React decides what to re-render.","react\u002Frendering-and-performance\u002Fvirtual-dom-reconciliation","Virtual DOM and Reconciliation","Rendering and Performance","rendering-and-performance","2026-06-24","Eg1x64GHznlb0XcJS2vShptyDF0E5StXiLMG8z1yzVw",[97,98,101,105,109],{"subtopic":91,"path":22,"order":21},{"subtopic":99,"path":100,"order":12},"React.memo","\u002Freact\u002Frendering-and-performance\u002Freact-memo",{"subtopic":102,"path":103,"order":104},"useMemo and useCallback Patterns","\u002Freact\u002Frendering-and-performance\u002Fusememo-usecallback-patterns",3,{"subtopic":106,"path":107,"order":108},"Code Splitting and Lazy Loading","\u002Freact\u002Frendering-and-performance\u002Fcode-splitting-lazy",4,{"subtopic":110,"path":111,"order":112},"Suspense and Concurrent Rendering","\u002Freact\u002Frendering-and-performance\u002Fsuspense-concurrent",5,{"path":114,"title":115},"\u002Fblog\u002Freact-virtual-dom-reconciliation-guide","React Virtual DOM and Reconciliation — A Complete Guide",1782244100932]