This article is published in English.
Svelte 5 Runes and SolidJS Signals: UI Updates Without Re-renders
See how Svelte 5 compiles runes and SolidJS wires signals to update the DOM directly, how that differs from React and Angular, and when switching is worth it.
Every React developer eventually has to explain why a component rendered four times when nothing visible changed, and why the fix involves memo, a dependency array and a stable callback. Svelte and SolidJS start from a different premise: if the framework knows exactly which piece of state feeds which piece of the DOM, it can update that one node and skip re-running components altogether. This article explains how each of them achieves that, what the code looks like in practice, where their model differs from React and Angular, and how to decide whether either one belongs in your next project.
The virtual DOM was a means, not an end
React's defining idea when it launched in 2013 was the virtual DOM. You describe the UI as a function of state. When state changes, React calls your component again, builds a fresh in-memory tree, compares it with the previous one, and applies only the differences to the real DOM.
That design made interfaces far more predictable than hand-written DOM manipulation, and it remains a sound model. It does carry a cost, though. The component function runs again whether or not its output needs to change, a new tree is allocated, and a diffing pass works out what actually changed, all of that to update a single <span> in the worst case. If you want the details of what the reconciler compares and why, the piece on how virtual DOM diffing works covers them.
Much of React's API surface since then, memo, useMemo, useCallback and most recently the React Compiler, exists to skip work the render-and-diff model would otherwise do. The compiler automates memoization so developers write less of it by hand, but the underlying model is unchanged: components re-run, and optimisation means persuading them not to. The article on what the React Compiler optimises and what it leaves to you goes into those limits.
Svelte and Solid ask a simpler question: what if the dependency between each piece of state and each DOM node were known precisely, so only that node is touched when the state changes?
Svelte: a compiler that writes the update code for you
Svelte is primarily a compiler. You author components in .svelte files, and at build time Svelte turns them into plain JavaScript that manipulates the DOM directly. There is no virtual DOM and no runtime diffing, and only a small runtime is shipped to the browser.
Since Svelte 5, reactivity is expressed through runes, explicit primitives the compiler recognises. The component below declares a piece of state and a value derived from it, then renders both in a button:
<script>
let count = $state(0);
let doubled = $derived(count * 2);
</script>
<button onclick={() => count++}>
{count} doubled is {doubled}
</button>
That is the entire component. There is no setter function and no dependency array. You increment count as if it were an ordinary variable, and because the compiler has already analysed which parts of the markup read count and doubled, it generates code that updates exactly those text nodes. $derived recalculates only when something it reads changes. Note that event handlers in Svelte 5 are regular attributes such as onclick, replacing the older on:click directive syntax.
Why teams like it
- Less code for the same result. Without hooks, setters or wrapper components, Svelte components usually come out noticeably shorter than their React equivalents. Less code tends to mean fewer places for bugs and quicker reviews.
- Small bundles. Because most of the work happens at compile time, the framework cost in the browser is low. For content sites, landing pages and anything where first load matters, that is a real business advantage.
- Familiar web building blocks. Markup, scoped styles and script live in one file and read like HTML, CSS and JavaScript. There are no JSX-specific conventions such as
className, which makes files approachable for designers and reviewers who do not write React.
For full applications, SvelteKit adds routing, server rendering and API endpoints, playing the role Next.js plays for React, with a reputation for lighter configuration.
One caveat is worth knowing early: runes are compiler features, so they only work inside .svelte files and in modules named with the .svelte.js or .svelte.ts suffix. Moving reactive logic into a plain .js utility file will not work without that naming.
SolidJS: JSX that runs once
Solid is easy to mistake for React at first glance. It uses JSX, it composes small functions, and a counter looks almost identical:
function Counter() {
const [count, setCount] = createSignal(0);
return (
<button onClick={() => setCount(count() + 1)}>
Count: {count()}
</button>
);
}
The difference that surprises React developers is that Counter runs exactly one time. Solid is built on fine-grained reactivity with signals. createSignal returns a getter and a setter, and the getter, count(), is a function call rather than a plain value. When JSX reads count() inside an expression, Solid records that this particular text node depends on that signal. Calling setCount later updates that text node and nothing else. The component function was only a setup step that connected signals to DOM nodes; it never runs again, so there is nothing to re-render.
This model is why Solid performs at or near the top of common framework benchmarks, often close to hand-written vanilla JavaScript. Treat any benchmark ranking as a snapshot and measure your own workload, but the architectural advantage is real: updates cost roughly in proportion to what changed, not to the size of the component tree.
Why teams like it
- No re-render mental model. The React question of why something rendered does not arise.
useMemo,useCallbackandReact.memohave no counterpart because there is nothing to skip. - Predictable effects. An effect runs when a signal it reads changes, not when a component happens to re-render and a dependency array allows it. Stale closures, a notorious React pitfall, largely disappear because values are always read fresh through getters.
- A gentle path from React. JSX and component composition carry over almost directly, so a React team mostly has to unlearn the painful parts rather than relearn everything.
The habits you have to unlearn
The run-once model has consequences that trip up newcomers. Destructuring props at the top of a component reads their values once and breaks reactivity, so props are usually accessed as props.name. Early returns and ternaries in the function body are evaluated only once, which is why Solid provides control-flow components such as Show and For. Once these rules click, they are consistent, but they are the main source of bugs for developers arriving from React.
How this compares with React and Angular
The deeper difference is philosophical rather than syntactic.
React made "re-run and diff" its core model and has spent years adding tools to reduce the cost of that model. It is still an excellent choice: its ecosystem is unmatched, hiring is straightforward, and the React Compiler genuinely reduces manual memoization. The trade-off is that you work within a model designed around the constraints of its era.
Angular is the full-featured enterprise option, with dependency injection, RxJS and strong conventions for nearly everything. It holds up very large codebases well, but the ceremony and learning curve are real. Its most significant recent changes, signals and zoneless change detection, move it toward fine-grained reactivity of the kind Solid popularised.
That convergence is visible across the industry. Angular adopted signals, React introduced a build-time compiler, newer frameworks such as Qwik build on fine-grained reactivity, and Vue, whose reactive refs were always close to signals, has been exploring compilation strategies of its own. It would be an overstatement to say Svelte and Solid invented every one of these ideas, but they demonstrated early and clearly that compilation and signals could carry an entire framework.
Why developers keep moving in this direction
Three unglamorous reasons explain most of the interest:
- Less framework to hold in your head. Attention goes to the product rather than to the framework's update semantics. Correctly memoizing a callback is nobody's idea of meaningful work.
- Performance by default. In React or Angular, a fast app is something you engineer with care. In Svelte or Solid, making an app slow usually takes some effort.
- Respect for your time. Smaller APIs, less boilerplate and fewer traps. Developer surveys have repeatedly placed both frameworks high on satisfaction and interest, while overall usage is still growing from a comparatively small base.
Should you switch?
For an existing product, almost certainly not right away. When a sizeable application already runs on React or Angular and the team knows that stack well, rewriting it is one of the most reliable ways to stall a project. Ecosystem size also matters: React has a library for almost any need, and although the Svelte and Solid ecosystems are healthy and growing, they are smaller, so check early that the pieces you depend on, such as component libraries, form tooling and authentication integrations, exist and are maintained.
The calculation changes for new work. A greenfield project, a performance-sensitive widget or an internal tool is a low-risk place to try one:
- choose Svelte if you want the friendliest learning curve and an experience that mostly just works
- choose Solid if your team thinks in JSX and wants maximum runtime performance with a React-shaped mental model
- stay with React or Angular when ecosystem breadth, hiring and existing expertise outweigh raw efficiency
Key takeaways
- React's render-and-diff model is predictable but does work proportional to the component tree; memoization and the React Compiler reduce that work without changing the model.
- Svelte 5 moves reactivity into the compiler through runes such as
$stateand$derived, generating direct DOM updates and shipping a small runtime. - Solid runs each component once and binds signals straight to DOM nodes, which removes re-renders but requires new habits around props and control flow.
- The wider ecosystem is converging on the same ideas: compile-time analysis, signals instead of re-renders, and direct updates instead of diffing.
- Adopt these frameworks where their strengths matter and the ecosystem covers your needs; do not rewrite a healthy codebase just to follow the trend.