Skip to content

Compiler vs Runtime

This is the biggest architectural difference between React and Svelte, and it shapes everything else. React is a runtime library — it ships to the browser and reconciles a virtual DOM on every state change. Svelte is a compiler — it analyses your components at build time and outputs plain JavaScript that updates the DOM directly. By the time your code reaches the browser, Svelte itself is largely gone.

React bundles a reconciler, a fiber scheduler, synthetic event system, and hooks runtime — all of this runs on every user’s device, on every page load. When state changes, React builds a new virtual DOM tree, diffs it against the previous tree, and then flushes the minimal set of real DOM changes. That diff step happens at runtime, in the browser.

Svelte analyses your component’s template and state at build time and generates targeted imperative DOM update code. When count changes, the compiled output calls textContent = count — one direct DOM write, no diffing, no reconciler involved. The Svelte “runtime” that ships is tiny (a handful of utility functions for things like {#each} keyed lists and transitions).

React (runtime)
// React — ships reconciler + fiber + hooks runtime
// On every setState:
// 1. Re-run the function component
// 2. Build a new virtual DOM tree
// 3. Diff against the previous tree
// 4. Flush real DOM changes
function Counter() {
const [count, setCount] = React.useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}
Svelte (compiled)
<!-- Svelte — compiles to direct DOM code -->
<!-- On count change, compiler emits roughly:
buttonText.data = "Count: " + count;
No diffing. No reconciler. -->
<script>
let count = $state(0);
</script>
<button onclick={() => count++}>
Count: {count}
</button>

Bundle size: A React + ReactDOM bundle is ~42 kB gzipped minimum, before your app. Svelte adds almost nothing — the output is your app code plus a tiny shared runtime.

Performance: No virtual DOM diffing means less work per state change. For most UIs this difference is imperceptible, but it becomes meaningful for large lists, animations, or low-end devices.

Mental model: You write declarative components exactly as you do in React. The compiler handles the imperative DOM work for you. You do not have to think about how it works — just know that count++ updates every place count appears in the template, automatically.

No stale closures from diffing: Because Svelte does not re-run your entire component function on each update (the way React re-renders a function component), the class of bugs around stale closures in React hooks does not exist in the same form in Svelte.

$derived is the Svelte equivalent of useMemo — it recomputes automatically when its dependencies change. No dependency array required; Svelte’s compiler tracks dependencies for you.

When does Svelte analyse your component and generate DOM update code?
React uses a virtual DOM primarily to:
What is the Svelte equivalent of React's useMemo?
Which statement about Svelte bundle size is correct?