FRAMEWORK-SPECIFIC

The same question, five frameworks

Five different answers to the same set of questions: how change is detected, how much work happens at compile time, and what actually reaches the DOM. Read a row across to orient yourself — then read the last block, which is where the row stops being true.

A tidy table implies an equivalence that does not exist. These are not five implementations of one idea; they are five different positions on what a framework is for, and the cells in a row are near-neighbours at best. The where this comparison misleads block on every row is the part worth reading, and it is the reason this page does not declare a winner.

Reactivity model
React
Re-render on state change: a component function runs again and produces a new description of its output.
Vue
Proxy-based reactive objects; reading a property inside an effect subscribes that effect to it.
Svelte
Compiler-tracked assignments (and explicit runes in the current version) turn a write into a targeted update.
Solid
Signals: reading a signal inside a tracking scope subscribes it, and a write notifies exactly those subscribers.
Angular
Historically zone-based change detection over the component tree; signals are now the direction of travel, and both coexist.
Where this comparison misleads

The word "reactive" covers two genuinely different mechanisms. React re-executes a component and works out what changed afterwards; Vue, Solid and modern Angular build a dependency graph and notify only what depends on the value. That difference decides what a performance problem looks like in each: in React it is a subtree that re-rendered when it did not need to, and in the signal-based systems it is a read that escaped tracking so nothing updated at all. Comparing them on a benchmark hides the fact that you debug them with different questions.

Rendering model
React
Virtual DOM diffed against the previous render, with the differences applied to the real DOM.
Vue
Virtual DOM, with compiler hints that let it skip static subtrees and patch only dynamic bindings.
Svelte
No virtual DOM: the compiler emits imperative code that updates the specific nodes an assignment can affect.
Solid
No virtual DOM and no component re-execution: components run once and signals update individual DOM bindings.
Angular
Template compilation into instructions that update bound expressions during change detection.
Where this comparison misleads

"Virtual DOM or not" is a weaker distinction than it sounds, because the DOM writes at the end are the same in all five — only the process of deciding which writes to make differs. Vue's compiler-informed virtual DOM sits between the two camps and is not well described by either label. Anyone claiming one column here is faster has to say faster at what workload, on what device: the gap is large in a rapid-update benchmark and often invisible in an ordinary form.

Compiler vs runtime work
React
Mostly runtime. JSX becomes function calls; the reconciler ships with the app. An optimising compiler is an emerging, optional part of the story.
Vue
Substantial compile-time analysis of single-file components, feeding hints to a runtime that still exists.
Svelte
Heavily compile-time: much of what other frameworks do at runtime is emitted as generated code instead.
Solid
Compile-time JSX transform into direct DOM creation, with a small signal runtime that stays.
Angular
Ahead-of-time template compilation, with a comparatively large framework runtime alongside it.
Where this comparison misleads

Compiling more is not automatically less: it moves cost from the runtime bundle into per-component generated code, so a framework with a small runtime can ship more total bytes in an application with very many components, and one with a large shared runtime amortises it. The crossover depends on the size and shape of the app, which is why "smallest framework" is a claim that changes sign as an application grows. Build-time work also has to happen on someone's machine, so it lands as CI and dev-server time instead.

State model
React
State lives in hooks inside components; sharing means lifting, context, or an external store, and there is no built-in answer for server state.
Vue
refs and reactive objects can live anywhere, inside or outside a component; Pinia is the conventional shared store.
Svelte
Component state plus stores; the current version generalises this into runes usable outside components too.
Solid
Signals and stores are independent of components by construction, so sharing is the default rather than an escalation.
Angular
Injectable services holding state, with dependency injection deciding scope and lifetime.
Where this comparison misleads

The frameworks disagree about whether state is fundamentally attached to a component. React's hooks are, so sharing is an escalation with its own re-render consequences; Solid's and Vue's primitives are not, so a shared value is just a value in a module. That changes which advice is even meaningful: "lift state up" is a React sentence, and the state-ownership question underneath it is universal while the mechanics are not portable.

Server rendering
React
Mature SSR and streaming, plus server components — largely delivered through meta-frameworks rather than the library itself.
Vue
SSR built into the core with Nuxt as the conventional full-stack layer.
Svelte
SvelteKit is effectively the way Svelte applications are built, with SSR as the default rather than an addition.
Solid
SolidStart provides SSR, streaming and islands, on a smaller and younger ecosystem.
Angular
Angular SSR with hydration, including incremental and event-replay hydration in recent versions.
Where this comparison misleads

You are usually comparing meta-frameworks, not frameworks: the SSR experience is Next, Nuxt, SvelteKit, SolidStart or Angular's own tooling, and their routing, data-loading and caching conventions differ far more than the rendering libraries beneath them. Migration cost lives in that layer, not in the component syntax. Server components in particular are a React-ecosystem architecture with no direct equivalent in the other columns, so the row is not comparing like with like.

Bundle cost
React
A runtime plus a reconciler, with per-component cost that is comparatively small.
Vue
A mid-sized runtime; the compiler removes some per-component work.
Svelte
A very small runtime, with generated code proportional to the number and complexity of components.
Solid
A very small runtime, with compile-time output creating DOM nodes directly.
Angular
A larger baseline that tree shaking reduces, and which amortises across a large application.
Where this comparison misleads

Baseline size is the number people quote and the least relevant one past a hello-world. What decides the shipped total is per-component output, how well your dependencies tree-shake, and whether the app is code-split at all — a single date or icon library routinely outweighs the entire framework. Comparing frameworks by their empty-app size answers a question nobody in production has.

Ecosystem
React
The largest by a wide margin: components, tooling, hiring pool, and the most third-party answers to any given problem.
Vue
Large and cohesive, with a strong first-party story for routing, state and build tooling.
Svelte
Smaller but growing, with SvelteKit covering most of what an app needs first-party.
Solid
Smallest of the five; excellent primitives, fewer off-the-shelf components.
Angular
Comprehensive and first-party: routing, forms, HTTP, i18n and testing all ship in the box.
Where this comparison misleads

Ecosystem size cuts both ways and the direction depends on your team. A large ecosystem means someone has solved your problem and also that there are six incompatible solutions with different maintenance states; a first-party-everything framework means fewer decisions and less choice when the built-in answer does not fit. The genuinely decisive factor is usually which one your team can maintain in three years, and that is not a property of the framework.

Learning curve
React
Small API surface, but a large body of conventions — hook rules, dependency arrays, identity stability — that are learned by hitting them.
Vue
Gentle start with templates and two coexisting component styles, which is itself something to learn.
Svelte
Very fast to start; the compiler-specific rules become visible later, and the runes change is a real relearning cost.
Solid
Familiar-looking JSX with different semantics underneath — components run once — which is the single most common source of early confusion.
Angular
Steepest initial curve: modules or standalone components, dependency injection, RxJS and a large surface to hold at once.
Where this comparison misleads

The curve is not one line, and "easy to start" and "easy to be correct in" are different properties that often trade against each other. Svelte and Solid are quick to pick up and have a specific rule you must internalise before anything works reliably; Angular front-loads the difficulty and then holds a large team to consistent conventions. Whether that is a cost or a benefit depends on how many people are in the codebase.

Typical failure mode
React
Re-rendering far more than necessary, then memoizing symptoms; stale closures over state captured in a previous render.
Vue
Losing reactivity by destructuring a reactive object; deep watchers doing more work than anyone intended.
Svelte
Updates that do not fire because the change was not an assignment the compiler could see, or reactivity crossing a module boundary.
Solid
A signal read outside a tracking scope, or a value destructured out of props, so a binding silently never updates.
Angular
Change detection running far too often over a large tree, or subscriptions that are never unsubscribed and leak.
Where this comparison misleads

This is the row that best shows why there is no winner: each framework has a characteristic bug that is a direct consequence of its model, and they are not the same bug. Re-render-too-much and never-update-at-all sit at opposite ends — the first is a performance problem you can see in a profile, the second is a correctness problem that looks like nothing happening. Choosing a framework is choosing which of these your team will spend the next three years learning to recognise.