
Svelte has changed more in the last two years than in the five before it. Svelte 5 became stable on 22 October 2024 with a new reactivity model built on runes, and SvelteKit 3 shipped on 1 October 2026 with a cleaner configuration story and stricter types. If you last looked at Svelte when `export let` and `$:` were the whole story, this guide covers what has actually changed, with short code examples and a practical view on whether to upgrade.
Everything here is checked against the official Svelte documentation, blog and changelogs. Features that are still experimental are labelled as such.
Svelte 5 in One Paragraph
Svelte is still a compiler: it turns your components into plain JavaScript at build time instead of shipping a large runtime to the browser. What changed in Svelte 5 is how you tell the compiler what is reactive. In Svelte 4, any top-level `let` was reactive and `$:` marked derived values and side effects. In Svelte 5, you use runes, explicit `$`-prefixed keywords the compiler understands. Under the hood, reactivity is now signal-based and fine-grained, so updates touch only what depends on the value that changed. The Svelte 5 release post describes the result as "almost completely backwards-compatible with Svelte 4".
Runes: The New Reactivity Model
$state and $derived
`$state` declares reactive state. `$derived` declares a value computed from other state, and Svelte keeps it up to date for you.
For arrays and plain objects, `$state` creates a deeply reactive proxy. That means `todos.push(...)` or `todos[0].done = true` update the UI without the old `todos = todos` reassignment trick. If you have large data that you only ever replace, `$state.raw` skips the proxy for better performance. For derived values that need more than one expression, use `$derived.by(() => { ... })`.
$effect
`$effect` runs side effects, such as talking to a third-party library or the canvas API. Svelte tracks the values you read inside it automatically, and you can return a cleanup function.
Two points from the `$effect` docs that catch people out:
- Effects only run in the browser, after the component is mounted, not during server-side rendering.
- Don't use `$effect` to keep one piece of state in sync with another. That is what `$derived` is for.
$props and $bindable
`export let` is replaced by destructuring `$props()`. Defaults, renaming and rest props all use normal JavaScript syntax. If a parent should be able to `bind:` to a prop, mark it with `$bindable`.
Runes outside components
Runes also work in `.svelte.js` and `.svelte.ts` files, and inside class fields. That means shared state no longer has to be a store.
One rule to remember: you can't export a `$state` variable that you reassign. Export an object and mutate its properties, or export getter functions.
Event Attributes Replace on:
In Svelte 5, event handlers are just props. `on:click={handler}` becomes `onclick={handler}`, and the shorthand `{onclick}` works when the names match.
This also changes how components send events to their parents. `createEventDispatcher` is deprecated. Instead, a component accepts a callback prop and calls it.
Event modifiers such as `|preventDefault` and `|once` are gone. Call `event.preventDefault()` inside the handler instead. Capture-phase listeners use a suffix, for example `onclickcapture`.
Snippets Replace Slots
Slots are deprecated in Svelte 5. Their replacement, snippets, are reusable chunks of markup that you declare with `{#snippet}` and render with `{@render}`. Content placed between a component's tags arrives as a `children` snippet prop.
Snippets can take parameters, which replaces the old `let:` slot props, and they can be passed to any component like any other prop. Because they are typed with `Snippet<[...]>`, TypeScript checks them too.
Other Svelte 5 Additions Worth Knowing
Svelte has shipped regular minor releases since 5.0. A few notable ones, from the Svelte changelog:
- `$props.id()` (5.20) generates IDs that match between server and client, useful for linking labels and inputs.
- Attachments (5.29) use `{@attach fn}` on elements. Unlike `use:` actions, they are fully reactive and re-run when the state they read changes.
- `await` in components (5.36) lets you use `await` directly in markup and script. This is still experimental and needs the `experimental.async` compiler option.
- `createContext` (5.40) gives you type-safe context without managing keys by hand.
- Components are functions, not classes. Use `mount()` and `hydrate()` from `svelte` instead of `new App(...)`.
TypeScript Is Native
Svelte 5 supports TypeScript natively, so you no longer need a preprocessor just to write `<script lang="ts">`. Types are simpler as well. `SvelteComponent` is deprecated in favour of the `Component` type, and `ComponentEvents` is no longer needed because events are now ordinary callback props with ordinary function types. SvelteKit 3 goes further and requires TypeScript 6 if you use TypeScript.
The sv CLI
Svelte 5 arrived alongside a new command line tool, `sv`, a toolkit for creating and maintaining Svelte apps. Its main commands are:
- `npx sv create my-app` scaffolds a new project.
- `npx sv add` adds integrations such as Tailwind CSS, ESLint, Vitest, Playwright or Drizzle to an existing project.
- `npx sv check` finds errors and warnings, including TypeScript errors, unused CSS and accessibility hints.
- `npx sv migrate` runs official migrations, such as `svelte-5` or `sveltekit-3`.
According to the October 2026 Svelte update, `sv` reached 1.0 on the same day as SvelteKit 3, which made its community add-on API official.
What's New in SvelteKit
SvelteKit 3
SvelteKit 3 was released on 1 October 2026. The Svelte team calls it "the same framework with a little more polish, a little more type safety, and a little less junk". The changes you will notice first, from the migration guide:
- Configuration lives in Vite. `svelte.config.js` is no longer supported. Options move into the `sveltekit()` plugin in `vite.config.js` or `vite.config.ts`.
- `$lib` becomes `#lib`. Imports use standard Node subpath imports declared in `package.json`, with explicit file paths.
- `$app/environment` is renamed `$app/env`, and environment variables move to `$app/env/public` and `$app/env/private`. The old `$env/...` modules are deprecated but still work.
- Param matchers are declared in a single `src/params.ts` file with `defineParams`, rather than one file per matcher.
- `invalidateAll` is deprecated in favour of `refreshAll`, and shallow routing moves into `goto()` with a `shallow` option.
- Higher minimums: Node 22.17, Svelte 5.57.1 and Vite 8, plus TypeScript 6 if you use TypeScript.
$app/state replaces $app/stores
SvelteKit 2.12 added `$app/state`, which exposes `page`, `navigating` and `updated` as rune-based reactive objects. In SvelteKit 3, `$app/stores` has been removed. You drop the `$` prefix and read properties directly.
`page.url` is now read-only, so copy it with `new URL(page.url.href)` before changing search parameters.
Remote functions (experimental)
Remote functions, available since SvelteKit 2.27, let you write a server function in a `.remote.ts` file and call it from a component as if it were local, with type safety across the network. They come in four flavours: `query` for reading data, `form` for progressively enhanced forms, `command` for writes from anywhere, and `prerender` for static data.
They are still experimental in SvelteKit 3. You need to enable `experimental.remoteFunctions` in the SvelteKit plugin, plus Svelte's `experimental.async` option. The SvelteKit 3 announcement says making them stable is the team's "top priority". They are worth trying on internal tools, but we would hold off on relying on them for a production client site until they are stable.
Should You Migrate?
The good news is that you don't have to migrate everything at once. Svelte 5 still compiles Svelte 4 syntax, and you can mix legacy components and rune components in the same app. Runes mode is switched on per component, as soon as it uses a rune.
Start new projects on Svelte 5 and SvelteKit 3. `npx sv create` gives you both by default, and all current documentation, tutorials and libraries target them.
For an existing Svelte 4 app, upgrade the dependency first and migrate components gradually:
- If you are on Svelte 3, move to Svelte 4 first, as the Svelte team recommends.
- Run `npx sv migrate svelte-5`. It bumps dependencies and converts components to runes, event attributes and snippets.
- Fix what the script can't convert. `createEventDispatcher` and `beforeUpdate`/`afterUpdate` need manual work, and the script may insert `run()` from `svelte/legacy` where it couldn't tell a derived value from a side effect. Replace those with `$derived` or `$effect`.
- Run `npx sv check` and your test suite, then click through the key flows.
You can also migrate one component at a time from the command palette in VS Code with the official Svelte extension.
For SvelteKit 2 apps, move to the latest 2.x release first, which shows targeted deprecation warnings. Then run `npx sv migrate sveltekit-3`, which handles most of the config, `#lib` and `$app/state` changes and generates a to-do list for the rest.
When to wait: if the app is stable, rarely changed and depends on a library that hasn't released a Svelte 5 version yet, there is little to gain from rushing. Note that the Svelte team has said Svelte 4 syntax support will eventually be phased out, with warning, so plan for it rather than ignore it.
When to move sooner: if the app is under active development, your team is fighting `$:` ordering bugs, or you want to share reactive state outside components, runes pay off quickly. The explicit model is also easier for new developers to read.
Building with SvelteKit
At Tech Inject we build fast, SEO-friendly websites and web apps with SvelteKit, including our own site, which runs on SvelteKit 3 and Svelte 5. If you are planning a new build or a Svelte 4 to Svelte 5 upgrade, see our web development services.
Sources
- Svelte 5 is alive (Svelte blog, 22 October 2024)
- Svelte 5 migration guide
- $state documentation
- $effect documentation
- Snippet documentation
- {@attach} documentation
- await expressions documentation
- Svelte changelog (GitHub)
- sv CLI overview
- SvelteKit 3 is here (Svelte blog, 1 October 2026)
- What's new in Svelte: October 2026
- Migrating to SvelteKit 3
- $app/state documentation
- Remote functions documentation
- SvelteKit changelog, version-2 branch (GitHub)
Frequently Asked Questions
Almost completely. Svelte 5 still compiles Svelte 4 syntax such as `export let`, `$:` and `on:click`, and you can mix old and new components in one app. A few edge cases changed, such as creating components with `new`, which is now `mount()`. The Svelte team plans to phase out the old syntax eventually, with advance warning.
Runes are compiler keywords that start with `$`, such as `$state`, `$derived`, `$effect`, `$props` and `$bindable`. They make reactivity explicit instead of relying on top-level `let` and `$:`. They work in components and in `.svelte.js` and `.svelte.ts` files, so you can share reactive state without stores.
Run `npx sv migrate svelte-5` in the project. It updates dependencies and converts components to runes, event attributes and snippets. You then need to replace `createEventDispatcher` with callback props, handle any `beforeUpdate` or `afterUpdate` code and review any `run()` calls the script inserted. Run `npx sv check` and your tests afterwards.
SvelteKit 3, released on 1 October 2026, moves configuration from `svelte.config.js` into `vite.config`, replaces `$lib` with `#lib`, renames `$app/environment` to `$app/env`, removes `$app/stores` in favour of `$app/state` and raises the minimum versions to Node 22.17, Svelte 5.57.1 and Vite 8. `npx sv migrate sveltekit-3` automates most of it.
Not yet. As of October 2026 remote functions are still experimental, even in SvelteKit 3. You enable them with `experimental.remoteFunctions` and Svelte's `experimental.async` option. The Svelte team has said stabilising them is its top priority.








