Chapter 14: Vue, Solid, Svelte, and the Reactivity Counter-Argument
React’s 2013 debut, described in the previous chapter, did two things at once. It introduced a style of frontend development that has dominated the field for the decade since. It also drew a particular line about how reactivity should work — the re-render the whole component when its state changes, then diff against a virtual DOM approach — and that line wasn’t the only one available.
Three frameworks that emerged in the years after React’s launch chose different lines. Vue, written by Evan You starting in 2013 and first released in 2014. Svelte, started by Rich Harris in 2016. Solid, started by Ryan Carniato in 2018. None of these frameworks tried to replace React on its own terms. Each one made an architectural choice that argued, implicitly or explicitly, that React’s model wasn’t the only reasonable way to build a frontend application.
This chapter is about what those choices were and what they teach us about the design space React’s default has been occupying.
Vue and Evan You
Section titled “Vue and Evan You”In 2013, Evan You was a designer at Google working on internal tools. The team used AngularJS — the original Angular, met in Chapter 11, released by Google in 2010 — and You’s experience was a common one for the era. AngularJS was powerful but heavy. The internal tools he was building used a small fraction of its capability and paid the full cost of its complexity. He started extracting the parts he found most useful — declarative templates with two-way binding, a component model, reactive data — into a small library he could drop into a page without committing to the full framework.
The library shipped publicly in February 2014 as Vue.js. The pitch was simple. Vue could be used as a complete framework, or you could include it in a single component of an existing page and let it manage just that region. The progressive adoption story was deliberate. You had seen too many teams forced to choose between use the whole framework everywhere or use nothing, and Vue’s design refused that binary.
Vue grew in two phases. The first phase, through Vue 2.x (2016), built a community around the library’s approachability and the quality of its documentation, which was widely regarded as among the best in the frontend ecosystem. Vue’s reactivity model — based on ES5 property getters that intercepted reads and writes on data objects — was simple to explain and, for most applications, simple to use. The Vue 2 template syntax (HTML augmented with directives like v-if, v-for, v-bind) was familiar to anyone who had used Angular, and the framework felt deliberately less academic than React.
The second phase, Vue 3 (released September 2020), rewrote the framework around a new reactivity system based on ES6 Proxies. The rewrite was a meaningful break — components could now be written in either the original Options API or the new Composition API, and the runtime characteristics changed significantly. The transition was, by framework-version-bump standards, relatively smooth, though it did require codebase migrations that some teams found painful.
Vue’s commercial position is unusual. It has never had a corporate sponsor in the way React has Meta or Angular has Google. Evan You runs Vue as an independent open-source project, funded primarily through donations, GitHub Sponsors, and the broader Vue ecosystem (training, conferences, the Nuxt meta-framework). Vue is the largest piece of independent frontend infrastructure that hasn’t been captured by a single company, and the project’s institutional independence is one of its quiet selling points.
In market terms, Vue’s adoption is largest in China, where it became the dominant frontend framework during the 2010s, and significant globally. The 2024 State of JS survey ranked Vue as the second most-used framework worldwide after React, ahead of Angular and Svelte. The position has been stable for years.
What Is Reactivity, Anyway
Section titled “What Is Reactivity, Anyway”Before we get to the frameworks that pushed back on React’s model, the chapter has to make a small definitional move.
Reactivity is the property of a system where outputs update automatically when inputs change. In a spreadsheet, when you change the value of cell A1, cell B1 (which is set to =A1*2) updates immediately without any explicit instruction. The spreadsheet is reactive. A frontend framework is reactive when the UI updates automatically as the application state changes, without the developer manually wiring each update.
Every modern frontend framework is reactive. They disagree about how.
React’s model is coarse-grained. When state inside a component changes, the component is re-executed top to bottom, the resulting virtual DOM tree is diffed against the previous virtual DOM tree, and the differences are applied to the real DOM. The unit of reactivity is the component. If a component renders a list of a thousand items and one item’s state changes, the entire component re-renders and the diffing process figures out that only one DOM node actually needs to change. The cost of this model is real (running the component function and diffing the result), but it’s been the basis of React’s mental model since 2013, and React’s optimizations (React.memo, useMemo, useCallback, the React Compiler) have all been variations on don’t run this re-render when nothing changed.
The frameworks that pushed back on React argued, with different specifics, that coarse-grained reactivity left performance on the table and made the developer write more memoization than the architecture should require.
Solid and Fine-Grained Reactivity
Section titled “Solid and Fine-Grained Reactivity”Ryan Carniato had been working on reactivity for a long time before Solid.
Carniato was active in the Marko community in the 2010s — Marko is a server-rendering framework originally built at eBay that pioneered a fine-grained reactivity model and a very small client runtime. His work there gave him a deep view of how reactive systems could be built without a virtual DOM. In 2018 he started Solid as a public exploration of those ideas, and the framework reached production-ready status with Solid 1.0 in 2021.
Solid’s central design choice is that components run once. A Solid component is a function that’s called the first time the component mounts, sets up the reactive graph, and returns. When state changes, the component function doesn’t run again — only the specific reactive expressions that depend on the changed state re-execute, and they update only the specific DOM nodes they’re responsible for.
A toy example illustrates the difference. In React:
function Counter() { const [count, setCount] = useState(0) return <button onClick={() => setCount(count + 1)}>{count}</button>}Every click runs the entire Counter function again. JSX is re-evaluated, a new virtual DOM node is produced, and the diffing pass figures out that only the text node inside the button has changed.
In Solid:
function Counter() { const [count, setCount] = createSignal(0) return <button onClick={() => setCount(count() + 1)}>{count()}</button>}The Counter function runs exactly once. The count() expression inside the JSX gets compiled into a reactive subscription that updates the text node directly when count changes. There’s no virtual DOM, no re-execution of the component, no diff. The cost of each state change is the cost of updating the DOM node that depends on it.
This is fine-grained reactivity, and Solid is its most rigorous current implementation in the JavaScript ecosystem. The trade-off is that the developer has to understand which values are reactive — count is a signal, not a value, and calling it (count()) is what subscribes to it — and the mental model takes some getting used to if you’ve been writing React. The payoff is performance characteristics React has to work hard to match, a much smaller runtime (Solid’s core is a few kilobytes), and a programming model that, once internalized, feels remarkably clean.
Solid hasn’t taken over the frontend. Its adoption is small relative to React or Vue. The reason it matters for this book is that it demonstrates, in production, that React’s coarse-grained model wasn’t the only option. The trade-offs are different, and reasonable engineering can make either choice.
Svelte and the Compile-Time Move
Section titled “Svelte and the Compile-Time Move”Rich Harris was a graphics editor at the Guardian and later the New York Times when he started Svelte.
The product context shaped the framework. News-graphics work requires shipping interactive visualizations on news sites, where the runtime cost of a framework matters enormously — a Guardian page can’t afford a 100KB framework runtime just to make one chart interactive. Harris’s original tool, Ractive.js (2013), was a more conventional reactive UI library aimed at this use case. Svelte, started in 2016 and reaching general adoption with Svelte 3 in 2019, took a more radical approach.
Svelte is a compiler. The framework you write against is a small declarative syntax (a superset of HTML, with embedded JavaScript and template directives). The compiler transforms that syntax into highly optimized vanilla JavaScript that updates the DOM directly. There’s no framework runtime at all in the traditional sense — or rather, the runtime is whatever code the compiler decides each component needs, and the unused parts of the framework are simply never included in the bundle.
<script> let count = 0</script>
<button on:click={() => count += 1}> {count}</button>The compiler reads this and produces JavaScript that does the minimum required work — a click handler that increments a variable and updates the corresponding text node. The Svelte runtime that ships with the application is whatever set of helpers this specific component needed; an application that doesn’t use Svelte’s transitions, animations, or stores doesn’t pay the cost of including them.
The compile-time approach has architectural implications beyond bundle size. Svelte components are reactive without explicit declarations — assigning to a variable is what triggers an update, and the compiler figures out what needs to re-render. This is, in many ways, the most ergonomic reactivity model of the three alternatives in this chapter. It’s also the most reliant on the compiler doing the right thing, and Svelte’s history includes a few moments where the compiler’s behavior was subtle in ways that caught teams off guard.
Svelte 5, released in 2024, restructured the reactivity model around explicit runes ($state, $derived, $effect) that look syntactically more like Solid’s signals. The shift was controversial within the Svelte community — it traded some of the original framework’s ergonomic magic for a more explicit and predictable mental model — but it brought Svelte’s design closer to the fine-grained reactivity story Solid had been arguing for.
Svelte’s commercial trajectory is also worth noting. Harris joined Vercel in 2021, and SvelteKit (the framework’s meta-framework, equivalent to Next.js for React) is now developed within Vercel’s open-source organization. The acquisition pattern — independent open-source project gets absorbed by a hosting company that monetizes by deploying applications built on it — is one we’ll return to in the meta-framework chapter. It’s the dominant business model in modern frontend infrastructure.
Reactivity as a Design Dimension
Section titled “Reactivity as a Design Dimension”The three frameworks in this chapter, plus React, give the field a working set of points on a design spectrum.
React occupies one end of that spectrum. Reactivity is coarse-grained, component-level, runtime, and explicit (you call setState; the framework re-renders the component). The virtual DOM and the diffing pass are the cost the model pays for its uniformity.
Solid occupies a different point. Reactivity is fine-grained, expression-level, runtime, and signal-based. The cost is the developer’s responsibility to understand the reactivity primitives; the benefit is direct DOM updates with no diffing overhead.
Svelte occupies yet another point. Reactivity is fine-grained (post-Svelte-5), compile-time-resolved, and the framework’s runtime ships only what each component actually uses. The cost is dependency on the compiler; the benefit is the smallest production runtime of the major frameworks.
Vue sits between Solid and React. Vue’s reactivity is fine-grained at the data level (Proxy-based observation tracks individual property reads and writes), but the rendering model is still component-based and uses a virtual DOM. The framework’s reactivity is more efficient than React’s by default, while keeping a developer experience that feels closer to React than Solid does.
None of these points is the right one. They’re trade-offs. Where in your application does the cost of reactivity matter? and what kind of mental model do you want your team to maintain? are questions with different right answers in different products. The book’s broader argument doesn’t require picking a winner among these four frameworks. It requires understanding that React’s defaults are defaults, not laws, and that the alternatives have been shipping in production for years.
Decoration Versus Replacement, Applied
Section titled “Decoration Versus Replacement, Applied”The decoration-vs-replacement framing — that an abstraction can either decorate a platform primitive with an opinion (without hiding it) or replace the primitive with a parallel reality the library owns — applies cleanly across this group.
React is the canonical replacement abstraction. A React-rendered button isn’t really a <button> you can target with document.querySelector and reliably manipulate from outside React; the framework owns its rendering, and bypassing the framework typically breaks something. The virtual DOM is, in effect, a parallel reality.
Vue is mixed. The component model and template syntax sit closer to replacement, but Vue’s reactivity (Proxy-based) decorates plain JavaScript objects without changing them structurally. Vue applications can usually be debugged with browser dev tools the way plain JavaScript can.
Solid is closer to decoration. A Solid component compiles down to direct DOM manipulation — the elements in the page are real elements, with no virtual-DOM intermediary. The reactivity primitives (signals, effects, memos) are explicit JavaScript values that can be inspected and manipulated.
Svelte is firmly on the decoration side, both because the compiler eliminates most framework abstractions before runtime and because the elements in the page are real DOM elements throughout the application’s lifetime.
This framing will become more important later in the book. The platform-first argument the rest of the book builds isn’t every framework is bad. It’s that decorating abstractions preserve the platform’s contracts while replacing abstractions impose their own, and the choice has architectural consequences. The frameworks in this chapter are some of the cleanest examples of decoration in modern frontend.
What Comes Next
Section titled “What Comes Next”This chapter has been about the alternatives that pushed back on React’s defaults. The next chapter is about what happened when the SPA model that React (and Vue, and Solid, and Svelte) had popularized started running into the limits of single-page applications at scale — and the meta-framework era began.
Exercise: Build the Same Counter Three Ways
Section titled “Exercise: Build the Same Counter Three Ways”Build a small counter component in React, Vue, and either Solid or Svelte. Make each version do the same thing — a button that displays a number and increments on click.
For each version, answer:
- How many times does the component function run when the user clicks the button?
- How does the framework know to update the displayed number?
- What’s the size of the production-built JavaScript bundle for the component? (Use the framework’s standard build tool — Vite, in most cases — and check the output size.)
- If you wanted to read the current count from outside the component, how would you do it?
- If the framework went away tomorrow, how much of your code could you keep using directly with the platform?
The point isn’t to declare a winner. The point is to feel the choices each framework has made, and to recognize that the choices are choices — not the only way the problem could be solved.