Skip to content

Why Svelte, the Compiler

Most UI frameworks are runtime libraries. You ship the framework to the browser, and at runtime it maintains a virtual DOM, re-runs your components when state changes, diffs the new virtual tree against the old one, and applies the differences. That machinery — the reconciler, the scheduler — is code the user downloads and the CPU runs on every update.

Svelte takes the work out of the browser and into the build step. The Svelte compiler reads your components ahead of time, figures out precisely which DOM nodes depend on which state, and emits plain JavaScript that updates exactly those nodes. There is no virtual DOM to ship, no diffing at runtime.

flowchart TB
  subgraph runtime["Runtime framework"]
    ship["ship framework + components"] --> vdom["runtime keeps a virtual DOM"]
    vdom --> diff["diff on every state change"]
  end
  subgraph svelte["Svelte (compiler)"]
    build["compile components at build time"] --> emit["emit precise update code"]
    emit --> tiny["ship tiny JS, no diffing"]
  end
Where the work happens: runtime vs compile time
  • Smaller bundles. You don’t ship a reconciler. A Svelte component compiles to roughly the imperative code you’d hand-write to update those exact nodes, so there’s little framework overhead riding along.
  • Less runtime work. When count changes, there’s no tree to re-create and diff — the generated code updates the one text node that shows count. Updates are surgical by construction.
  • Less indirection. The gap between “what I wrote” and “what runs” is small: the compiler’s output is close to what you’d write by hand.

This is why Svelte is sometimes called “the disappearing framework” — much of it compiles away, leaving mostly your logic plus a thin helper layer.

Given a component with let count = $state(0) and <p>{count}</p>, the compiler statically sees that the text node depends on count. It generates code that (1) creates the <p> once, and (2) registers that when count changes, this specific text node’s content is updated — nothing else. Multiply that across a whole app and you get updates that touch only what changed, without any runtime bookkeeping to figure out what changed.

<script>
let count = $state(0);
</script>
<!-- The compiler knows this text node — and only this one — depends on count. -->
<button onclick={() => count++}>clicked {count} times</button>

You write declarative components; the compiler writes the imperative DOM code. That division of labor is the entire value proposition.

Where does a runtime framework do its diffing work?
Why are Svelte bundles typically small?
When `count` changes in Svelte, what happens?
Why is Svelte called "the disappearing framework"?