Skip to content
All writing

What Is Svelte? Svelte 5, Runes and How It Compares to React

· Updated · 7 min read

Contents
  1. What Svelte is
  2. Svelte 5 and runes
  3. How Svelte differs from React
  4. SvelteKit
  5. What I measured
  6. When I pick it

Svelte is a UI framework built around a compiler. You write components in HTML, CSS and JavaScript, and the build step turns them into the JavaScript that updates the page. I use it with SvelteKit for this site and for Convetta, and what keeps me there is how little ceremony sits between a component and what it does.

What Svelte is

Rich Harris published the first version in November 2016, and it is MIT licensed. A component lives in a single .svelte file with markup, styles and logic together. It is not a niche project any more: the npm package shows roughly 6 million downloads a week as of this month. Styles are scoped to the component by default, so you do not need a CSS-in-JS library for isolation.

<script>
  let count = $state(0);
</script>

<button onclick={() => count++}>
  clicks: {count}
</button>

That is a complete, reactive component. The official overview describes the compiler as turning declarative components into lean, tightly optimised JavaScript. There is still a small runtime in the output, but there is no virtual DOM. The compiler works out at build time which state touches which part of the page.

Svelte 5 and runes

Svelte 5 became stable in October 2024 and the team calls it a ground-up rewrite. The visible change is runes: functions whose names start with a dollar sign and which tell the compiler what is reactive.

  • $state declares reactive state. Arrays and plain objects become deep proxies, so todos[0].done = true and list.push(item) both update exactly what depends on them.
  • $derived declares a value computed from other state.
  • $effect runs side effects, such as drawing on a canvas or calling a library, after the DOM updates.
  • $props declares what a component receives from its parent.

Of the four, $effect is the one to use least. The docs advise against updating state inside effects, because it tends to produce convoluted code and endless update loops, and effects only run in the browser, never during server rendering. When I reach for an effect, I first ask whether the thing is really a derived value. Usually it is.

The reasons are in the release post. In Svelte 4, changing one property of a reactive object invalidated the whole object, because that was all the compiler could see. The $: label also covered two different jobs, derived values and side effects, and dependencies were fixed when the code was compiled instead of when it ran. Runes separate those jobs. The migration guide says that about 90 percent of the time you want $derived, and my own code agrees: Convetta has 13 $derived against 4 $effect.

Because runes are plain function calls, they also work outside components. A .svelte.js file can hold reactive state and share it between components, which is where Svelte 4 pushed you toward stores. The old syntax still compiles, and old and new components can sit in one project, so an upgrade does not have to happen in a day.

Composition changed too. Svelte 4 treated event handlers and slotted content as separate concepts from props, a legacy of aligning with web components in 2019 that the release post calls a mistake. In Svelte 5 an event handler is a prop that holds a function, and reusable chunks of markup are snippets that you define once in a component and render with {@render}. A parent can also pass a snippet to a child as a prop. This is the part of Svelte 5 I find easiest to read, because a component’s whole interface is its props.

One gotcha worth knowing: destructuring a reactive value copies it at that moment, as in normal JavaScript, and the copy stops updating. The $state docs call this out.

Svelte also lets you use await in the script block, inside $derived and in markup, since version 5.36. It is still experimental and needs experimental.async in the compiler options. The flag is due to be removed in Svelte 6, so I would not build on it before then.

How Svelte differs from React

The difference that matters in daily work is when your code runs. A React component is a function that runs again on every render, and you manage that with hooks and dependency arrays. In Svelte, code in a component runs once, at creation. After that, the compiled output updates only the places that depend on the state you changed. There is no dependency array on $effect, because Svelte records what the effect read while it ran.

Coming from React, the first week is mostly unlearning. State is a plain variable that you assign to, so there is no setter function, and a derived value is declared once instead of recomputed in the render body. The other friction is older material. Many tutorials and answers still show the Svelte 4 syntax (let as implicit state, $:, export let), and it still compiles, so check which syntax an example uses before copying it.

The boilerplate gap is smaller than it was. React Compiler reached 1.0 in October 2025 and memoizes components automatically, which removes much of the manual useMemo and useCallback work. React itself is at 19.3. If your complaint about React is hook ceremony, check it against a current setup before switching.

Speed is a weaker argument than it used to be. The Svelte team itself wrote that other frameworks had adopted signal-based fine-grained reactivity and passed Svelte 4 in performance, and Svelte 5 is built on the same idea. Real applications are mostly limited by data loading, images and the libraries you add, so I would not pick a framework for a benchmark number.

The virtual DOM split is also narrowing. Vue 3.6 adds Vapor Mode, an opt-in compilation mode for single-file components that skips the virtual DOM. At the time of writing it is in release candidate and not yet in a stable release.

Where React still wins is everything around the framework. It has the larger ecosystem, many more ready-made libraries, a bigger hiring pool, and React Native for mobile. In Svelte you will more often write the thing yourself. Whether that trade is worth it depends on how big the team is and how much of the product is plumbing you would rather not write.

SvelteKit

Svelte renders components and nothing more. SvelteKit is the application framework on top of it, with routing, data loading, server rendering and prerendering. It plays the role Next.js plays for React and Nuxt for Vue. Adapters decide where the result runs, whether that is a Node server, a static host or an edge platform, and you can choose per route whether a page is rendered on the server, in the browser or at build time.

This site uses adapter-static and ships as plain HTML, CSS and JavaScript with no server. Convetta uses adapter-node and runs in Docker. Its pages are prerendered, and the JPG, PNG and WEBP conversion happens in the browser, so those files never leave the visitor’s machine. Both run on Svelte 5.57.1.

What I measured

I measured the production build of Convetta’s home page on October 10, 2026. It loads 12 JavaScript files totalling about 163 kB, or about 61 kB gzipped. That includes the SvelteKit client runtime and router along with the application’s own code, so it tells you what a real SvelteKit page weighs and does not isolate Svelte itself. If bundle size decides your choice, build your own project and read the numbers there.

When I pick it

For a site or product that one person or a small team will maintain, I pick Svelte. The components stay short, reactivity is visible in the file, and one official framework covers routing and rendering. I would pick React for a large team that already knows it, for anything that shares code with React Native, or when a specific library only exists for React.

To try it, create a SvelteKit project with the official CLI:

npx sv create my-app
cd my-app
npm install
npm run dev

The playground runs in the browser if you would rather start with one component.