Comparisons

Pairs that get conflated in real conversations, and in real pull requests. Neither column wins — what decides is the requirement. Each record leads with the confusion, because the confusion is the reason the record exists.

Virtual DOM vs Fine-grained reactivity

What people get wrong about this pair

"The virtual DOM is fast" is the claim, and it is not a claim at all until you name what it is faster than and at what. It is faster than naively rebuilding the DOM by hand on every state change, because DOM mutation and the layout it triggers are the expensive part and diffing avoids most of it. It is not faster than not diffing — a fine-grained system that knows which text node changed does strictly less work than one that re-runs a component and compares two trees to find out. What the virtual DOM buys is a programming model: render as a pure function, no subscription bookkeeping, and no rules about how you may read a value. The costs are real on both sides. The diffing model spends main-thread time proportional to the re-rendered subtree, which is why memoization and keys exist and why an unmemoized provider can re-render half an app. The fine-grained model has almost no diff cost but the reactivity is *where you read the value*, so destructuring a value out of its tracking context silently breaks updates, and dependency graphs can be harder to debug. Neither is the winner; they fail differently, and the failures land on different people.

Virtual DOM — re-run the component, diff a description of the output, patch the difference
Use it when

A model where rendering is a pure function of state and the framework decides what changed. Predictable, easy to reason about as a mental model, and the cost is a diff proportional to the tree you re-rendered.

Fine-grained reactivity — a dependency graph updates exactly the bindings that changed
Use it when

A model where reading a value subscribes to it, so a change notifies precisely the computations and DOM bindings that depend on it, with no component re-execution and no diff.

DimensionVirtual DOM — re-run the component, diff a description of the output, patch the differenceFine-grained reactivity — a dependency graph updates exactly the bindings that changed
Unit of updateA component re-render, then a diffA single binding or computation
Work on a state changeProportional to the re-rendered subtreeProportional to the number of dependents
Mental modelUI is a pure function of stateValues are observable and reads create subscriptions
Tuning toolsKeys, memoization, splitting componentsRarely needed; correctness of tracking matters more
Typical bugRe-rendering far more than necessaryA read that escaped tracking, so nothing updates
Runtime costA reconciler that ships with the appA smaller runtime, often with more compile-time work
DOM writesOnly the diff is applied — this part is the sameOnly the changed binding is applied
Fair comparison needsA workload, a device and a device classThe same three — a benchmark without them says nothing