The virtual DOM is a plain JavaScript object tree that mirrors the structure of the real DOM without any of its cost. Building one, and comparing it to the previous version, is cheap — plain object creation and comparison. Touching the real document is not: every real DOM write can trigger layout recalculation and repainting. React and similar frameworks exploit that gap. Instead of updating the real DOM every time something changes, they build a new virtual tree, diff it against the last one, and apply only the minimum set of real changes the diff turns up.
What changed in 2026
- React's fiber-based scheduler kept maturing. The core diffing concept has not changed, but how that work gets prioritized and interrupted for urgent updates continues to improve.
- Signal-based reactivity gained mainstream attention as a real alternative, not a niche approach. Fine-grained reactive frameworks now get evaluated directly against virtual DOM frameworks in most comparisons.
- The "virtual DOM equals fast" claim got more thoroughly debunked in mainstream performance writing. The more accurate framing is that avoiding unnecessary re-renders is the actual lever, and the virtual DOM is one way to manage that, not a guarantee.
- Compiler-based optimization blurred old category lines. Compile-time optimizations that auto-memoize components narrow the gap between virtual DOM frameworks and compiled, signal-based ones.
Why the virtual DOM exists
Direct DOM manipulation is expensive per operation, especially when many small updates happen close together. The virtual DOM turns imperative, one-at-a-time DOM edits into a different workflow: build an entire new in-memory tree describing what the UI should look like, diff it against the previous tree, and apply only the computed minimal patch to the real DOM. The expensive part — touching the real document — happens once, batched, instead of scattered across dozens of individual edits.
How diffing actually works
- A state change triggers a re-render, producing a brand new virtual tree.
- The new tree is compared to the previous virtual tree, node by node.
- Elements are matched primarily by type and position, or by an explicit key.
- Only the nodes that actually differ get a real DOM operation queued.
- All the queued operations are applied together in a batch.
{items.map((item) => (
<Row key={item.id} data={item} />
))}
The key lets the diffing algorithm match an element across renders by identity instead of position. Without a stable key, reordering, inserting, or removing items in a list can cause the wrong element, including its internal state, to be updated.
Virtual DOM versus the alternatives
| Approach |
How it updates the DOM |
Example |
| Virtual DOM diffing |
Builds a new in-memory tree, diffs it, patches only what changed |
React |
| Fine-grained signals |
Tracks exactly which DOM node depends on which piece of state, updates it directly |
SolidJS |
| Compiled reactivity |
Compiles away most of the runtime, generating direct update calls ahead of time |
Svelte |
| Direct DOM manipulation |
You update specific nodes yourself, with no framework layer in between |
Vanilla JS |
The performance myth, addressed directly
The virtual DOM was never really a speed trick. A hand-optimized, carefully targeted direct DOM update, or a fine-grained signal that updates exactly one node, can both outperform a virtual DOM diff, because they skip building and comparing an entire in-memory tree. What the virtual DOM actually buys you is a simpler mental model: describe what the UI should look like for the current state, and let the diff figure out what to touch, instead of tracking every individual mutation by hand. The real performance lever, in any of these approaches, is still avoiding unnecessary work in the first place — the same principle behind debounce vs throttle for limiting how often a handler fires at all.
FAQ
Does the virtual DOM make React faster than plain JavaScript?
Not automatically, and not always. Careful, targeted direct DOM updates can outperform a virtual DOM diff, because they skip building and comparing an in-memory tree entirely. The virtual DOM trades some raw speed for a simpler, declarative way to describe UI that stays fast enough for most apps by default.
Why do frameworks like Svelte and SolidJS not use a virtual DOM?
They track dependencies at a finer grain, down to the individual piece of state and the exact DOM node it affects, so they can update that node directly without comparing an entire tree first. This skips the diffing step altogether.
Why does React ask for a key prop on list items?
The key lets the diffing algorithm match an element across renders by identity instead of position. Without a stable key, reordering, inserting, or removing list items can cause the wrong element to be updated, including its internal state.
Is the virtual DOM the same thing as the shadow DOM?
No, despite the similar name. The shadow DOM is a real browser feature for encapsulating a piece of markup and style. The virtual DOM is a JavaScript-only concept a framework uses to compute updates efficiently, and it has nothing to do with the browser's shadow DOM.
Where to go next