Verdict
sv migrate sveltekit-3 upgraded every package and moved the config correctly, but left three removed options in place, so the first build failed three times in a row. Worse, it rewrote a working {base}/about link into one that 404s, and that passed both the build and svelte-check.
Tested on
- SvelteKit
- 2.40.0 → 3.0.1
- Svelte CLI
- sv 1.1.1
- Vite
- 7.3.7 → 8.3.3
- Node
- 24.14.0
- OS
- Windows 11 Pro
Contents
- What changed in the packages01
- Way 1: just bump the package02
- Way 2: the official migration03
- The build failed three times04
- The bug the build didn’t catch: a link that 404s05
- A behaviour change: fail() now returns its status code06
- TypeScript 7 with SvelteKit 307
- Checklist after sv migrate sveltekit-308
- Limits of this test09
- How this was tested10
SvelteKit 3.0 shipped on October 1, 2026, with about thirty breaking changes and an official migration command. I took a SvelteKit 2.40 app of the kind most people still run (Vite 7, TypeScript 5.9, a svelte.config.js, and a few of the APIs that changed), confirmed it built and type-checked with 0 errors, then upgraded it the two ways people will try.
What changed in the packages
SvelteKit 3.0.1 refuses to install next to older tooling. Its peer requirements:
| Package | Needs |
|---|---|
vite |
^8.0.12 |
svelte |
^5.57.1 |
typescript |
^6.0.0 |
@sveltejs/vite-plugin-svelte |
^7.0.0 |
@sveltejs/adapter-auto |
8.0.0 (version 6 and 7 only accept Kit 2) |
It also needs Node 22 or newer.
Way 1: just bump the package
npm install -D @sveltejs/kit@latest
npm error code ERESOLVE
npm error Found: @sveltejs/vite-plugin-svelte@6.2.4
npm error Could not resolve dependency:
npm error peer @sveltejs/vite-plugin-svelte@"^7.0.0" from @sveltejs/kit@3.0.1
Nothing is installed. You’d have to bump Vite, Svelte, TypeScript, the Vite plugin and the adapter together, which is what the migration tool does for you.
Way 2: the official migration
npx sv migrate sveltekit-3The new Svelte CLI (sv 1.0, released the same day as Kit 3) offers ten tasks. Its own advice is that “most projects need all applicable tasks”, so I ran them all (--tasks all). It finished with All tasks applied successfully! and:
- upgraded all seven packages to the versions in the table above,
- moved
svelte.config.jsintovite.config.ts, inside thesveltekit({ … })plugin call, - rewrote
$app/pathsimports (base,resolveRoute→resolve), - moved the
Handletype import to@sveltejs/kit/hooks, - replaced
tsconfig.jsonwith one that extends$app/tsconfig, - added
#libsubpath imports topackage.json, - wrote a
MIGRATION_TASKS.mdlisting work it didn’t automate.
Then I ran the build.
The build failed three times
Each fix revealed the next error.
1. The CSRF option. The tool moved it into vite.config.ts unchanged:
config_option_removed_check_origin
`config.csrf.checkOrigin` has been removed in favour of `csrf.trustedOrigins`
error during build:
[Error: Failed to load SvelteKit options from Vite config]
2. The preload option. Same story, and this one isn’t mentioned in MIGRATION_TASKS.md at all:
config_option_removed_preload_strategy
`config.output.preloadStrategy` has been removed. `modulepreload` will always be used
error during build:
[Error: Failed to load SvelteKit options from Vite config]
3. The Node polyfills. Left in hooks.server.ts:
"./node/polyfills" is not exported under the conditions ["module", "node", "production", "svelte", "import"]
from package …\node_modules\@sveltejs\kit
Delete all three. None has a replacement you need for a normal app:
csrf: { checkOrigin: true }: remove it. Checking the origin is the default. Only listcsrf.trustedOriginsif other sites must be allowed to post your forms.output: { preloadStrategy: … }: remove it. SvelteKit 3 always usesmodulepreload.import { installPolyfills } from '@sveltejs/kit/node/polyfills'and theinstallPolyfills()call: remove both. Node 22+ already has what it provided.
The bug the build didn’t catch: a link that 404s
My Kit 2 page had a normal base-path link:
<a href="{base}/about">About</a>
On Kit 2.40 it rendered href="/about". The migration rewrote it to:
<a href="{resolve('')}/about">About</a>
On Kit 3.0.1 that rendered as href=".//about", which a browser resolves to http://localhost/ + //about, a 404. From a nested page like /blog/post it becomes /blog//about. It passed vite build and svelte-check with no warning. The tool did list +page.svelte under “Files to review” in MIGRATION_TASKS.md, but only as a generic $app/paths check, after already writing the broken version.
Search your project for resolve('') after migrating. Put the whole path inside resolve:
<a href={resolve('/about')}>About</a>A behaviour change: fail() now returns its status code
My form action returns fail(400, { missing: true }) when a field is empty. Same action, same empty field:
| Kit 2.40 | Kit 3.0.1 | |
|---|---|---|
| Plain form POST (no JS) | HTTP 200 | HTTP 400 |
use:enhance (fetch) |
HTTP 200, "status":400 inside the JSON |
HTTP 400 |
The page and the returned form data look the same, so users won’t notice. Things that read the HTTP status will: end-to-end tests asserting 200, uptime or error-rate monitoring, and logs that treat 4xx as errors.
TypeScript 7 with SvelteKit 3
Kit 3’s typescript: ^6.0.0 excludes TypeScript 7. The same side-by-side setup from my TypeScript 7 frameworks test works here too:
npm install -D "@typescript/native@npm:typescript@7.0.2" "typescript@npm:@typescript/typescript6@^6.0.2"
Checklist after sv migrate sveltekit-3
- Run
vite buildbefore anything else, and fix config errors one by one (checkOrigin,preloadStrategyand any other removed option). - Delete
@sveltejs/kit/node/polyfillsimports. - Search for
resolve('')and move the path insideresolve(…). - Work through
MIGRATION_TASKS.mdand any@migration-taskcomments. - Check tests and monitoring that expect
200from failed form actions. - Run the app and click the links. A green build proved nothing about item 3.
Limits of this test
One small app, built to touch the changes most likely to bite: config options, $app/paths, hooks and form actions. It didn’t cover remote functions, shallow routing, parameter matchers, custom adapters or $app/stores, which the migration also rewrites. The release notes list about thirty breaking changes. Read the official migration guide for the rest.
How this was tested
A SvelteKit 2.40.0 app (adapter-auto 6.1.1, Vite 7.3.7, vite-plugin-svelte 6.2.4, TypeScript 5.9.3) that built and passed svelte-check with 0 errors, upgraded with npm install and then with npx sv@1.1.1 migrate sveltekit-3 --tasks all. After each change I ran vite build and svelte-check, then served both versions with vite preview and checked the rendered links and the form action’s HTTP status with curl. Every message above is copied from those runs.
— N.K., end of entry No.044