diagram.mmd — flowchart
Virtual DOM Diffing flowchart diagram

Virtual DOM diffing is the process by which JavaScript frameworks like React compare a new in-memory tree of UI description objects against the previous tree to determine the minimal set of real DOM mutations required to bring the page up to date.

The core insight is that real DOM mutations are expensive: each write can trigger style recalculation, layout, and paint (see DOM Update Lifecycle). By maintaining a lightweight JavaScript object representation of the UI — the virtual DOM — the framework can perform comparisons entirely in memory at the cost of JavaScript execution rather than browser rendering. Only the differences (the diff) are applied to the actual DOM.

When a component's state or props change, the framework calls the component's render function to produce a new virtual DOM tree. It then runs the reconciliation algorithm to compare the new tree against the previous virtual DOM snapshot. React's reconciler uses a heuristic O(n) algorithm rather than the theoretically optimal O(n³) tree-edit-distance algorithm, because the optimal approach is too slow for real-time UIs. The key heuristics are: two elements of different types produce completely different trees (the old subtree is unmounted and a new one mounted); elements of the same type are updated in place; and key props on list items allow the reconciler to match nodes across positions without re-mounting them. This is why missing or unstable key values cause list performance issues.

Once the diff is computed, the framework generates a patch — a list of DOM operations such as setAttribute, removeChild, appendChild, and textContent updates. These are applied in a single batch, minimizing forced synchronous layouts. Libraries like Inferno and Preact use similar vdom diffing strategies with different performance tradeoffs.

Understanding diffing explains why React.memo, shouldComponentUpdate, and useMemo matter: they short-circuit the render phase entirely when inputs haven't changed, preventing even the virtual DOM work. See React Rendering Lifecycle for where these optimizations hook in.

Free online editor
Edit this diagram in Graphlet
Fork, modify, and export to SVG or PNG. No sign-up required.
Open in Graphlet →

Frequently asked questions

Virtual DOM diffing is the process by which a JavaScript framework compares a new in-memory tree of UI objects (the virtual DOM) against the previous snapshot to find the minimum set of real DOM operations needed to update the page. It trades JavaScript computation for fewer, cheaper DOM mutations.
React uses an O(n) heuristic reconciliation algorithm. It compares nodes at the same level by type: if types differ, the old subtree is fully replaced. If types match, the existing DOM node is updated in place. For lists, `key` props allow the reconciler to match nodes across positions — without keys, React assumes positional identity and can incorrectly reuse or re-mount nodes.
Virtual DOM diffing adds a JavaScript overhead step before every DOM update. For very large, mostly-static trees, this overhead can exceed the cost of the DOM mutations it saves. Fine-grained reactive frameworks like Vue 3 (with its compiler-level static tree hoisting) and Svelte (which compiles away the virtual DOM entirely) can outperform vdom diffing in these cases.
The virtual DOM is a plain JavaScript object tree that describes the desired UI state — it has no rendering cost to create or modify. The real DOM is the browser's live document tree; mutations to it can trigger style recalculation, layout, and paint. The virtual DOM acts as a buffer: the framework diffs two cheap JS trees and produces a minimal patch, then applies only the necessary changes to the expensive real DOM.
mermaid
flowchart TD A[State or Props Change] --> B[Call Component Render Function] B --> C[Produce New Virtual DOM Tree] C --> D[Compare Against Previous Virtual DOM] D --> E{Same element type?} E -- No --> F[Unmount Old Subtree] F --> G[Mount New Subtree to Real DOM] E -- Yes --> H{Props or children changed?} H -- No --> I[Skip — No DOM mutation] H -- Yes --> J[Compute Attribute and Child Diffs] J --> K{List with keys?} K -- Yes --> L[Match Nodes by Key] K -- No --> M[Match Nodes by Position] L --> N[Generate Minimal Patch List] M --> N N --> O[Apply Patches to Real DOM] G --> P[Frame Updated] O --> P style P fill:#22c55e,color:#fff
Copied to clipboard