The Svelte team released SvelteKit 3.0 on October 1, 2026, seven weeks after the release candidate went out on August 13. The sv CLI reached 1.0 the same day. The team describes the new major as the same framework with "a little more polish, a little more type safety, and a little less junk."
The junk it removed sits in your codebase, though. A typical SvelteKit 2 app has a svelte.config.js, $lib imports and a $app/stores import or two, and SvelteKit 3 changes all of them.
Minimum versions
| Dependency | Minimum |
|---|---|
| Node | 22.17 |
| TypeScript | 6 |
| Svelte | 5.57.1 |
| Vite | 8.0.12, the first Vite 8 release with stable Rolldown 1 |
| @sveltejs/vite-plugin-svelte | 7 |
The Svelte 5 requirement pays for the error-handling change. SvelteKit 2 still supported Svelte 4, which had no error boundaries, so +error.svelte caught errors thrown during load and missed render errors. SvelteKit 3 catches rendering errors too, sends errors you raise with error(...) through handleError, and applies sourcemaps to stack traces.
Run the migration
The team recommends upgrading to the latest 2.x release first, so you see the deprecation warnings, then running the codemod:
npx sv migrate sveltekit-3 --tasks all --confirm
It rewrites what it can and leaves a TODO list for the rest. You will want to review three changes by hand.
Config lives in the Vite plugin
svelte.config.js no longer works. Options that sat under kit become top-level options of the sveltekit() plugin:
// vite.config.js
import { defineConfig } from 'vite';
import { sveltekit } from '@sveltejs/kit/vite';
import adapter from '@sveltejs/adapter-auto';
export default defineConfig({
plugins: [
sveltekit({
compilerOptions: { experimental: { async: true } },
adapter: adapter()
})
]
});
Several options went away in the move. preloadStrategy is gone because SvelteKit uses modulepreload in all cases, prerender.origin became paths.origin, and csrf.checkOrigin became csrf.trustedOrigins. If you run adapter-node behind a proxy, paths.origin replaces the ORIGIN environment variable.
$lib is a subpath import
SvelteKit stopped generating the $lib alias. You declare #lib in package.json, and Node, Vite and TypeScript resolve it without framework glue:
{
"imports": {
"#lib": "./src/lib/index.js",
"#lib/*": "./src/lib/*"
}
}
Imports need file extensions now, so $lib/foo becomes #lib/foo.js. Your tsconfig.json also changes, from extending ./.svelte-kit/tsconfig.json to extending $app/tsconfig, with explicit include and exclude arrays.
Removed modules
| SvelteKit 2 | SvelteKit 3 |
|---|---|
| $app/stores | removed, use $app/state |
| $app/environment | renamed to $app/env |
| $service-worker | removed, use $app/env, $app/manifest and $app/paths |
| base, assets, resolveRoute in $app/paths | removed, use asset() and resolve() |
| $env/* modules | deprecated in favor of $app/env/private and $app/env/public |
Behavior changes that won't show up as errors
A few changes compile fine and still alter what users see.
version.pollIntervaldefaults to one hour, soupdated.currentflips on its own after a deploy. If you show an update banner, expect it to appear more often.- Shallow routing moved to
goto(url, { shallow: true }), and shallow navigations now firebeforeNavigate,onNavigateandafterNavigate. If your analytics run in those hooks, filter on theshallowproperty. gotorejects URLs that don't match a route in your app.preloadDatareturns{ type: 'error' }for failed pages instead of a 200loadedresult.- Forms using
use:enhancewith anactionon another page now navigate there.
Adapter users have their own list. The Cloudflare adapter removed the platform object in favor of an emulated cloudflare:workers module, and adapter-node serves static assets present at build time and nothing added later, with content-hash ETags.
Remote functions are still experimental
Remote functions, SvelteKit's type-safe client-server calls, stay behind an experimental flag and need Async Svelte, which has its own flag. The team calls them its top priority. Don't plan a rewrite of load functions and form actions around them yet.
This week
- Upgrade to the newest 2.x and clear the deprecation warnings.
- Check Node in CI and production. Anything below 22.17 blocks the upgrade.
- Run
npx sv migrate sveltekit-3 --tasks all --confirmon a branch, then work through the TODO list. - Search for
$app/stores,pushStateandinvalidateAll, and test navigation-heavy pages by hand.
If you deploy to Cloudflare and read bindings from platform, budget extra time for that adapter change before you merge.
Volodymyr Chornous

