What Frameworks Actually Buy You

Comparing popular JavaScript frameworks against plain vanilla JavaScript is a useful exercise, especially when trying to figure out what problems they genuinely solve and what they ask in return. The four frameworks examined here — React, SolidJS, Svelte, and Lit — all claim to make UI development more declarative and reactive, but they go about it in very different ways, with very different trade-offs.

React’s pitch is declarative views that are predictable and easy to debug. SolidJS follows similar philosophy but ditches the virtual DOM for a more direct implementation. Svelte compiles your code at build time into something that updates the DOM surgically. Lit sits on top of the Web Components standards and adds reactivity plus declarative templates.

Data-Binding And Reactivity

Data binding is a declarative way to express how state syncs between a model and a UI. Every popular UI framework starts its tutorial with this pattern, and each has its own syntax. In JSX (React and SolidJS), data binding is expressed inline in the template. Lit uses a similar declarative template syntax, while Svelte uses its own compile-time directives to accomplish the same thing.

Reactivity is the engine that makes data binding work efficiently. When the model changes, something has to propagate that change to the DOM. This is where the frameworks diverge most.

React uses a virtual DOM. It renders the whole view, compares the output to the previous result, and applies only the difference to the actual DOM. This adds a layer of abstraction between your state and the DOM, which is why ReactDOM itself ships around 120 KB of minified JavaScript.

SolidJS takes a more explicit approach. Instead of diffing virtual DOM trees, it uses a store and built-in elements like Show that track changes internally. This removes the need for the virtual DOM machinery entirely, and the minified library weighs in at roughly 18 KB.

Svelte moves the work to a compile step. The compiler knows which events can trigger state changes and generates plain, imperative JavaScript that directly performs the DOM updates. The framework itself is tiny when shipped because most of the logic ends up as generated code tailored to your specific app.

Lit leans on the browser’s native support for HTML custom elements. Reactivity is expressed through element properties, and the minified library is around 16 KB.

Conditionals And Lists

Beyond basic data binding, every framework offers primitives for the "if" and "for" logic that UIs inevitably need.

Conditionals — React expresses conditional rendering directly in JSX with JavaScript if or ternary expressions. SolidJS comes with a Show component that handles this in a more dedicated way. Svelte has an #if directive in its template syntax. Lit uses an explicit ternary inside the render function, which keeps it close to plain JavaScript.

These are functionally similar, but the implementation cost changes the bundle size and the way you reason about scheduling. React’s reconciliation runs through its virtual DOM queue. SolidJS's Show is tracked natively. Svelte transposes #if into generate code. Lit evaluates your expression in the render path.

Lists — Efficient list rendering is harder than it looks, because you don’t want to replace an entire list just because one item changed.

React relies on a special key attribute to tell its diffing engine which list items correspond to which DOM nodes. SolidJS provides for and index components that track identity and updates internally, avoiding the virtual DOM entirely. Svelte uses an each directive that the compiler turns into explicit update code based on the expressions it sees. Lit exports a repeat function that takes a keying function, similar in spirit to React's key prop.

The underlying problem is the same for everyone: map data to the DOM efficiently without throwing away work. The difference is whether this logic lives inside a heavy runtime, inside compiled code, or inside a thin template library.

The Real Cost Of A Framework

Frameworks deliver data-binding, conditional logic, and list handling, but they aren’t free. Beyond the obvious bundle size, there are three costs worth highlighting.

Bundle size — ReactDOM leads the pack at roughly 120 KB minified. SolidJS and Lit are both around the 16–18 KB range. Svelte’s own code is about 2 KB, but that number is misleading because its compiler emits additional JavaScript proportional to your app’s complexity. The point stands: heavier abstraction, like the virtual DOM, costs kilobytes, and those kilobytes affect parse and execution time on the client.

Build complexity — Svelte and SolidJS require a compile step at build time, which means transpilers, bundlers, and configuration overhead. Lit does not require a compilation step for the core reactivity, but its template engine lacks dedicated tooling so debugging is on your own. The more expressive the build pipeline, the more infrastructure you have to maintain just to run your code.

Debuggability — The code you execute is not the code you wrote. React’s scheduler allocates its own call stack, which is fine until an infinite loop or an unexpected re-render needs to be explained. Svelte generates custom code for each app, so your stack traces will reference compiled artifacts rather than your sources. With Lit, you need to understand the internal mechanics of the template engine to step through issues.

Upgrades are the fourth quiet cost. Framework updates that break your code without changes on your side represent effort lost to somebody else’s roadmap. Browsers also change, but platform fixes are broadly applied and well documented.

The lessons here will apply directly when we look at replacing these features with something more disciplined than a framework. This discussion continues in Part 2, but the takeaway already feels clear: the problems frameworks solve are real, and the solutions they pick carry weight.