Migrating to SvelteKit v3

next.svelte.dev · 2026-10-01 · by Long Nguyen ·generic
Migrating to SvelteKit v3 • SvelteKit documentation

Migrating to SvelteKit v3

SvelteKit 3 removes some legacy features, moves configuration out of `svelte.config.js` into the Vite plugin, and raises the minimum version of certain dependencies. For many of these breaking changes, you can automatically migrate:

npx sv migrate sveltekit-3

We recommend upgrading to the most recent 2.x version before upgrading to 3.0 so that you can take advantage of targeted deprecation warnings.

Updated dependencies[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Updated-dependencies)

SvelteKit 3 requires the following minimum versions:

  • Node v22.17
  • TypeScript v6
  • Svelte v5.57.1
  • Vite v8.0.12 (the first Vite 8 release bundling stable `rolldown` v1)
  • `@sveltejs/vite-plugin-svelte` v7
  • Update the versions in your `package.json` and run your package manager’s install command.

    Configuration[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Configuration)

    svelte.config.js is no longer supported[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Configuration-svelte.config.js-is-no-longer-supported)

    Instead of declaring project configuration in `svelte.config.js`, it must now be passed to the `sveltekit` Vite plugin in `vite.config.js`. Options that previously lived under `config.kit.*` are now top-level plugin options, alongside things like `compilerOptions`:

    vite.config

    import { defineConfig(alias) function defineConfig(config: UserConfig): UserConfig (+5 overloads)
    import defineConfigType helper to make it easier to use vite.config.ts
    accepts a direct 
    {@link 
    UserConfig
    }
     object, or a function that returns it.
    The function receives a 
    {@link 
    ConfigEnv
    }
     object.
     } from 'vite';
    import { sveltekit(alias) function sveltekit(config?: Config): Promise<Plugin[]>
    import sveltekitThe SvelteKit Vite plugin, which must be added to your vite.config.js file along with your project's configuration:
    /// file: vite.config.js
    import adapter from '@sveltejs/adapter-auto';
    import { sveltekit } from '@sveltejs/kit/vite';
    import { defineConfig } from 'vite';

    export default defineConfig({ plugins: [ sveltekit({ adapter: adapter(), compilerOptions: { experimental: { async: true } }, experimental: { remoteFunctions: true } }) ] });As well as SvelteKit, the plugin options are used by other tooling that integrates with Svelte such as editor extensions. Any options that don't belong to SvelteKit are passed through to vite-plugin-svelte, so you can set options like inspector here too. The experimental namespace is shared — SvelteKit reads its own flags and forwards the rest.

    [!LEGACY] Prior to SvelteKit 3, config lived in a svelte.config.js file, which is no longer supported. The ability to configure SvelteKit via vite.config.js was added in version 2.62.

    reference } from '@sveltejs/kit/vite'; import adapter(alias) function adapter(): Adapter import adapter from '@sveltejs/adapter-auto';

    export default defineConfig(alias) defineConfig(config: UserConfig): UserConfig (+5 overloads) import defineConfigType helper to make it easier to use vite.config.ts accepts a direct {@link UserConfig } object, or a function that returns it. The function receives a {@link ConfigEnv } object. ({ plugins(property) UserConfig.plugins?: PluginOption[] | undefinedArray of vite plugins to use. : [ sveltekit(alias) sveltekit(config?: Config): Promise<Plugin[]> import sveltekitThe SvelteKit Vite plugin, which must be added to your vite.config.js file along with your project's configuration: /// file: vite.config.js import adapter from '@sveltejs/adapter-auto'; import { sveltekit } from '@sveltejs/kit/vite'; import { defineConfig } from 'vite';

    export default defineConfig({ plugins: [ sveltekit({ adapter: adapter(), compilerOptions: { experimental: { async: true } }, experimental: { remoteFunctions: true } }) ] });As well as SvelteKit, the plugin options are used by other tooling that integrates with Svelte such as editor extensions. Any options that don't belong to SvelteKit are passed through to vite-plugin-svelte, so you can set options like inspector here too. The experimental namespace is shared — SvelteKit reads its own flags and forwards the rest.

    [!LEGACY] Prior to SvelteKit 3, config lived in a svelte.config.js file, which is no longer supported. The ability to configure SvelteKit via vite.config.js was added in version 2.62.

    reference({ compilerOptions(property) compilerOptions?: Omit<CompileOptions, "filename" | "format" | "generate"> | undefinedThe options to be passed to the Svelte compiler. A few options are set by default, including dev and css. However, some options are non-configurable, like filename, format, generate, and cssHash (in dev). https://svelte.dev/docs/svelte/svelte-compiler#CompileOptions : { experimental(property) experimental?: { async?: boolean; } | undefinedExperimental options 5.36 : { async(property) async?: boolean | undefinedAllow await keyword in deriveds, template expressions, and the top level of components 5.36 : true }}, adapter(property) Config.adapter?: Adapter | undefinedYour adapter is run when executing vite build. It determines how the output is converted for different platforms. undefined : adapter(alias) adapter(): Adapter import adapter() }) ] });

    See the configuration docs for further examples.

    Removed options[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Configuration-Removed-options)

    The following options are obsolete and should be removed from your `vite.config.js`:

  • `files.lib` (details)
  • `experimental.handleRenderingErrors` is no longer required (details)
  • `experimental.instrumentation` is no longer required (details)
  • `experimental.tracing` is now a top level `tracing` option (details)
  • `vitePlugin` is removed — pass `vite-plugin-svelte` options like `inspector` directly to the plugin instead
  • `preloadStrategy` is removed — `modulepreload` is now supported everywhere and so is always used
  • `prerender.origin` is removed in favour of `paths.origin`
  • `csrf.checkOrigin` is removed in favour of `csrf.trustedOrigins`
  • Added options[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Configuration-Added-options)

  • `output.linkHeaderPreload` determines whether to use `Link` HTTP headers to preload resources like `.js` and `.css` files rather than injecting `<link>` elements in the rendered HTML. This can cause issues when the headers grow too large, so SvelteKit 3 uses `<link>` elements by default instead.
  • `csrf.trustedOrigins` allows you to specify external origins that are allowed to make form submissions.
  • `paths.origin` replaces `prerender.origin`, and should reflect your app’s public-facing origin if it can’t reliably be derived from request headers (for example because it’s behind a reverse proxy). It will be used for CSRF checks on form submissions and remote function calls. If using `adapter-node`, this replaces the `ORIGIN` environment variable.
  • Changed options[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Configuration-Changed-options)

  • `version.pollInterval` now defaults to one hour, meaning SvelteKit will periodically check for new deployments and set updated.current to `true` accordingly. Previously, no polling occurred by default.
  • $lib is now #lib[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$lib-is-now-lib)

    The `$lib` alias is no longer generated automatically by SvelteKit. It is replaced by a `#lib` alias that you declare in the imports field of your `package.json`, leveraging Node’s built-in subpath imports (which Vite and TypeScript resolve natively). Add this to your `package.json`...

    package

    {
    	"imports": {
    		"#lib": "./src/lib/index.js",
    		"#lib/": "./src/lib/"
    	}
    }

    ...and replace `$lib` with `#lib` across your codebase. Note that you will also have to add the module extensions (e.g. `.js` or `.ts`) to these imports.

    import { foo } from '$lib/foo';
    import { fooimport foo } from '#lib/foo.js';

    $app/environment (renamed)[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-environment-%28renamed%29)

    The `$app/environment` module has been renamed to $app/env. It can now be imported inside your service worker, where previously if you needed to access `version` you would use the now-removed $service-worker module.

    $app/forms[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-forms)

    Forms with use:enhance that specify an `action` on a different page will navigate to that page upon submission, rather than staying on the current page. This ensures that the enhanced behaviour more closely matches the native, non-enhanced behaviour.

    $app/manifest[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-manifest)

    A new $app/manifest module gives you access to metadata about your app. You can import this anywhere in your app, including in service workers for offline caching purposes, for which you would previously use the now-removed $service-worker module.

    $app/navigation[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-navigation)

    Changes to shallow routing[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-navigation-Changes-to-shallow-routing)

    For shallow routing, `pushState/replaceState` are deprecated in favor of goto:

    // instead of this...
    pushState('/foo', state);
    replaceState('/bar', state);

    // ...do this: goto(alias) goto(url: string | URL, opts?: GotoOptions): Promise<void> import gotoAllows you to navigate programmatically to a given route, with control over details such as whether scroll and focus are reset (as they would be with a regular navigation) or preserved. Returns a Promise that resolves when SvelteKit navigates (or fails to navigate, in which case the promise rejects) or the state change has been applied. goto is intended for navigations to routes that belong to the app, and will reject if a route cannot be resolved. For external URLs, use window.location = url to perform a full-page navigation instead of calling goto(url). url Where to navigate to. Note that if you've set config.paths.base and the URL is root-relative, you need to prepend the base path if you want to navigate within the app. opts Options related to the navigation reference('/foo', { shallow(property) GotoOptions.shallow?: boolean | undefinedIf true, updates the URL and page.state without navigating. false : true, state(property) GotoOptions.state?: App.PageState | undefinedAn optional object that will be available as page.state. }); goto(alias) goto(url: string | URL, opts?: GotoOptions): Promise<void> import gotoAllows you to navigate programmatically to a given route, with control over details such as whether scroll and focus are reset (as they would be with a regular navigation) or preserved. Returns a Promise that resolves when SvelteKit navigates (or fails to navigate, in which case the promise rejects) or the state change has been applied. goto is intended for navigations to routes that belong to the app, and will reject if a route cannot be resolved. For external URLs, use window.location = url to perform a full-page navigation instead of calling goto(url). url Where to navigate to. Note that if you've set config.paths.base and the URL is root-relative, you need to prepend the base path if you want to navigate within the app. opts Options related to the navigation reference('/bar', { shallow(property) GotoOptions.shallow?: boolean | undefinedIf true, updates the URL and page.state without navigating. false : true, replace(property) GotoOptions.replace?: boolean | undefinedIf true, replaces the current history entry rather than creating a new one. false : true, state(property) GotoOptions.state?: App.PageState | undefinedAn optional object that will be available as page.state. });

    A new `persistState: true` option will cause `page.state` to be reapplied following a page reload.

    Shallow routing now triggers navigation hooks (beforeNavigate, onNavigate and afterNavigate). You can filter them out by checking the `shallow` property of the object passed to those navigation hooks.

    invalidateAll is deprecated in favour of refreshAll[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-navigation-invalidateAll-is-deprecated-in-favour-of-refreshAll)

    `invalidateAll` is deprecated in favour of refreshAll. The difference is that `refreshAll` does not reset `page.state` to an empty object, which is usually what you want when using shallow routing.

    Additionally, calling `invalidateAll()` or `invalidate(...)` during an in-flight navigation no longer aborts that navigation.

    goto options are updated[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-navigation-goto-options-are-updated)

    In addition to the new `shallow` option described above, various goto options have changed:

  • `invalidateAll` is now `refreshAll`, to mirror the above change
  • `keepFocus: true` and `noScroll: true` have been combined as `reset: false`
  • `replaceState` is now `replace`
  • goto rejects for URLs that don’t resolve to a route[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-navigation-goto-rejects-for-URLs-that-don't-resolve-to-a-route)

    `goto(...)` now rejects when called with a URL that does not resolve to a route within the app, matching the existing behaviour for external URLs. To navigate to an external URL, use `window.location.href = url`.

    delta only exists for popstate navigations[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-navigation-delta-only-exists-for-popstate-navigations)

    The `delta` property on navigation events (beforeNavigate, onNavigate and afterNavigate) now only exists for `popstate` navigations (back/forward). It is `undefined` for all other navigation types.

    preloadData can return an error result[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-navigation-preloadData-can-return-an-error-result)

    `preloadData(...)` now returns `{ type: 'error', status, error }` when the target page fails to load, instead of returning `{ type: 'loaded' }` with a 200 status. The `'redirect'` result now also includes the correct `status`. Add an `error` branch to any code that consumes the result:

    const resultconst result: ({
        type: "loaded";
        data: Record<string, any>;
    } | {
        type: "redirect";
        location: string;
    } | {
        type: "error";
        error: App.Error;
    }) & {
        status: number;
    } = await preloadData(alias) preloadData(href: string): Promise<({
        type: "loaded";
        data: Record<string, any>;
    } | {
        type: "redirect";
        location: string;
    } | {
        type: "error";
        error: App.Error;
    }) & {
        status: number;
    }>
    import preloadDataProgrammatically preloads the given page, which means

    ensuring that the code for the page is loaded, and calling the page's load function with the appropriate options.

    This is the same behaviour that SvelteKit triggers when the user taps or mouses over an <a> element with data-sveltekit-preload-data. If the next navigation is to href, the values returned from load will be used, making navigation instantaneous. Returns a Promise that resolves with the result of running the new route's load functions once the preload is complete. href Page to preload reference(urlconst url: "/somewhere");

    if (resultconst result: ({ type: "loaded"; data: Record<string, any>; } | { type: "redirect"; location: string; } | { type: "error"; error: App.Error; }) & { status: number; }.type(property) type: "loaded" | "redirect" | "error" === 'loaded') { // ... } else if (resultconst result: ({ type: "redirect"; location: string; } & { status: number; }) | ({ type: "error"; error: App.Error; } & { status: number; }).type(property) type: "redirect" | "error" === 'error') { // do something in case of an error }

    $app/paths[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-paths)

    base, assets, and resolveRoute removed[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-paths-base-assets-and-resolveRoute-removed)

    The deprecated `base`, `assets`, and `resolveRoute` exports have been removed from $app/paths. Use asset and resolve instead:

    // instead of this...
    const pathname = base + resolveRoute('/blog/[slug]', { slug });
    const file = assets + '/foo.png';

    // ...do this: const pathnameconst pathname: string = resolve(alias) resolve<"/blog/[slug]">(route: "/blog/[slug]", params: Record<string, string>): ResolvedPathname import resolveResolve a pathname by prefixing it with the base path, if any, or resolve a route ID by populating dynamic segments with parameters. In hash routing mode, the returned URL starts with #. During server rendering, the base path is relative and depends on the page currently being rendered. import { resolve } from '$app/paths';

    // using a pathname const resolved = resolve(`blog/hello-world`);

    // using a route ID plus parameters const resolved = resolve('/blog/[slug]', { slug: 'hello-world' });2.26 reference('/blog/[slug]', { slug(property) slug: string }); const fileconst file: string = asset(alias) asset(file: AssetPath): string import assetResolve the URL of an asset in your static directory, by prefixing it with config.paths.assets if configured, or otherwise by prefixing it with the base path. During server rendering, the base path is relative and depends on the page currently being rendered. <script> import { asset } from '$app/paths'; </script>

    <img alt="a potato" src={asset('potato.jpg')} />2.26 reference('foo.png');

    The `Pathname` and `Asset` types have also been renamed to `Path` and `AssetPath`, and the leading `/` has been removed from those types — so `asset('/foo.png')` should now be `asset('foo.png')`, and pathnames passed to `resolve` no longer start with `/` (e.g. `resolve('blog/hello-world')`). Only route IDs start with `/` now.

    Service workers can now import $app/paths[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-paths-Service-workers-can-now-import-$app-paths)

    Previously, you needed to import `base` from the now-removed $service-worker module. You can now use asset(...) and resolve(...) from `$app/paths`.

    $app/service-worker[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-service-worker)

    A new $app/service-worker provides type-safe access to the service worker execution context in your `src/service-worker/index.ts`, provided you have a `src/service-worker/tsconfig.json` that extends $app/tsconfig/service-worker.

    $app/state[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-state)

    page.url is now readonly[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-state-page.url-is-now-readonly)

    `page.url` is now typed as a `ReadonlyURL` with `ReadonlyURLSearchParams`, so mutating it — e.g. `page.url.searchParams.set(...)` or assigning to `page.url.pathname` — is now a type error. If you need a mutable URL, copy it first:

    const urlconst url: URL = new URLvar URL: new (url: string | URL, base?: string | URL) => URLThe URL interface is used to parse, construct, normalize, and encode URLs. It works by providing properties which allow you to easily read and modify the components of a URL.
    MDN Reference
    (pageany.urlany.hrefany);
    urlconst url: URL.searchParams(property) URL.searchParams: URLSearchParamsThe searchParams read-only property of the URL interface returns a URLSearchParams object allowing access to the GET decoded query arguments contained in the URL.
    MDN Reference
    .set(method) URLSearchParams.set(name: string, value: string): voidThe set() method of the URLSearchParams interface sets the value associated with a given search parameter to the given value. If there were several matching values, this method deletes the others. If the search parameter doesn't exist, this method creates it.
    MDN Reference
    ('q', 'svelte');

    updated updates automatically[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-state-updated-updates-automatically)

    The `updated.current` property becomes `true` when SvelteKit detects that a new version of the app has been deployed. Previously, this would only happen following a manual `updated.check()`, or after a failed navigation. In SvelteKit 3 it happens more frequently:

  • on any navigation that results in data being fetched from the server
  • on any remote function call
  • when the window becomes visible or focused (e.g. when switching back from another tab)
  • after a polling interval (which now defaults to one hour)
  • Note that if you use a feature like Vercel’s skew protection, passive detection on navigation and remote functions may report false negatives, since the request will be handled by the earlier deployment. Polling and event-based checks will still work, since they bypass skew protection.

    $app/stores (removed)[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-stores-%28removed%29)

    The `$app/stores` module (which exports the `$page`, `$navigating`, and `$updated` stores) has been removed. Use $app/state instead, which provides fine-grained Svelte 5 state, and remove the `$` prefix when reading values (i.e. `page` rather than `$page`):

    <script>
    	import { page } from '$app/state';
    </script>

    <p>current pathname: {page.url.pathname}</p>

    $app/tsconfig[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-tsconfig)

    Your project’s `tsconfig.json` should now extend `$app/tsconfig` rather than `./.svelte-kit/tsconfig.json`. It should also specify `include` and `exclude` arrays, as `$app/tsconfig` does not specify these:

    tsconfig

    {
    	"extends": "$app/tsconfig",
    	"include": ["src", "test", "*"],
    	"exclude": ["src/service-worker"]
    }

    Some essential `compilerOptions` (`isolatedModules` and `verbatimModuleSyntax`) are included in `$app/tsconfig`, alongside various options that are strongly recommend but which can be overridden in your own config.

    $app/tsconfig/service-worker[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-tsconfig-service-worker)

    Your service worker needs to be part of a separate TypeScript project, otherwise the types for things like `fetch` events will be incorrect. To do this, exclude the service worker from your project’s root `tsconfig.json`, and add a `src/service-worker/tsconfig.json` that extends `$app/tsconfig/service-worker`:

    src/service-worker/tsconfig

    {
    	"extends": "$app/tsconfig/service-worker"
    }

    $env/... (deprecated)[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$env-%28deprecated%29)

    The various `$env/...` modules have been deprecated in favour of `$app/env/private` and `$app/env/public` — see Environment variables for more details.

    $service-worker (removed)[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#$service-worker-%28removed%29)

    The `$service-worker` module has been removed. Import `version` from $app/env, `assets`, `immutable` and `prerendered` from $app/manifest, and `resolved` from $app/paths instead.

    @sveltejs/kit[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#sveltejs-kit)

    error, isHttpError, redirect, and isRedirect refer to public types[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#sveltejs-kit-error-isHttpError-redirect-and-isRedirect-refer-to-public-types)

    `error`, `isHttpError`, `redirect`, and `isRedirect` now refer to the public types rather than the internal classes. If you were importing the internal `HttpError` / `Redirect` classes from `@sveltejs/kit/internal`, or doing `instanceof` checks against them, use `isHttpError`/`isRedirect` from `@sveltejs/kit` instead.

    json and text are deprecated[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#sveltejs-kit-json-and-text-are-deprecated)

    The `json(...)` and `text(...)` helpers for generating responses are deprecated. Use `Response.json(...)` and `new Response(text)` instead.

    defineParams moved to @sveltejs/kit/params[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#sveltejs-kit-defineParams-moved-to-sveltejs-kit-params)

    The `defineParams` function for creating param matchers, along with the associated types, now live in @sveltejs/kit/params.

    Env-related types moved to @sveltejs/kit/env[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#sveltejs-kit-Env-related-types-moved-to-sveltejs-kit-env)

    Types like `EnvVarConfig`, used with defineEnvVars hook, now live in `@sveltejs/kit/env`.

    Hooks-related types moved to @sveltejs/kit/hooks[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#sveltejs-kit-Hooks-related-types-moved-to-sveltejs-kit-hooks)

    Types like `Handle`, which defines the type of your handle hook, now live in `@sveltejs/kit/hooks`.

    Remote function types moved to $app/server[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#sveltejs-kit-Remote-function-types-moved-to-$app-server)

    Types describing remote functions, such as `RemoteQuery`, `RemoteForm` and `RemoteCommand`, now live in `$app/server` alongside the functions themselves.

    @sveltejs/kit/hooks[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#sveltejs-kit-hooks)

    The `defineEnvVars` function has moved from `@sveltejs/kit/hooks` to `@sveltejs/kit/env`.

    @sveltejs/kit/node[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#sveltejs-kit-node)

    The getRequest and setResponse helpers are now synchronous and no longer return Promises. Remove `await` from calls in custom Node servers or adapters.

    @sveltejs/kit/node/polyfills (removed)[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#sveltejs-kit-node-polyfills-%28removed%29)

    The `@sveltejs/kit/node/polyfills` module (and the Node global shims in `adapter-node` and `adapter-netlify`) applied to Node versions that are no longer supported. Remove any `import '@sveltejs/kit/node/polyfills'` statements from your custom server code.

    Security[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Security)

    csrf.checkOrigin replaced by csrf.trustedOrigins[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Security-csrf.checkOrigin-replaced-by-csrf.trustedOrigins)

    The deprecated `csrf.checkOrigin` option has been removed. CSRF protection is always on; instead of disabling it with `checkOrigin: false`, allow trusted cross-origin hosts with `csrf.trustedOrigins`.

    vite.config

    export default defineConfig(alias) defineConfig(config: UserConfig): UserConfig (+5 overloads)
    import defineConfigType helper to make it easier to use vite.config.ts
    accepts a direct 
    {@link 
    UserConfig
    }
     object, or a function that returns it.
    The function receives a 
    {@link 
    ConfigEnv
    }
     object.
    ({
    	plugins(property) UserConfig.plugins?: PluginOption[] | undefinedArray of vite plugins to use.
    : [
    		sveltekit(alias) sveltekit(config?: Config): Promise<Plugin[]>
    import sveltekitThe SvelteKit Vite plugin, which must be added to your vite.config.js file along with your project's configuration:
    /// file: vite.config.js
    import adapter from '@sveltejs/adapter-auto';
    import { sveltekit } from '@sveltejs/kit/vite';
    import { defineConfig } from 'vite';

    export default defineConfig({ plugins: [ sveltekit({ adapter: adapter(), compilerOptions: { experimental: { async: true } }, experimental: { remoteFunctions: true } }) ] });As well as SvelteKit, the plugin options are used by other tooling that integrates with Svelte such as editor extensions. Any options that don't belong to SvelteKit are passed through to vite-plugin-svelte, so you can set options like inspector here too. The experimental namespace is shared — SvelteKit reads its own flags and forwards the rest.

    [!LEGACY] Prior to SvelteKit 3, config lived in a svelte.config.js file, which is no longer supported. The ability to configure SvelteKit via vite.config.js was added in version 2.62.

    reference({ csrf(property) Config.csrf?: { checkOrigin?: boolean; trustedOrigins?: string[]; } | undefinedProtection against cross-site request forgery (CSRF) attacks. : { checkOrigin: false trustedOrigins(property) trustedOrigins?: string[] | undefinedAn array of origins that are allowed to make cross-origin form submissions to your app. Each origin should be a complete origin including protocol (e.g., https://payment-gateway.com). This is useful for allowing trusted third-party services like payment gateways or authentication providers to submit forms to your app. If the array contains '*', all origins will be trusted. This is generally not recommended!

    [!NOTE] Only add origins you completely trust, as this bypasses CSRF protection for those origins.

    CSRF checks only apply in production, not in local development. [] ['https://checkout.stripe.com', 'https://accounts.google.com']: ['https://trusted-site.com'] } }) ] });

    Cross-origin form submissions require a Content-Type header[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Security-Cross-origin-form-submissions-require-a-Content-Type-header)

    Cross-origin mutative requests that omit a `Content-Type` header are now rejected as CSRF. Ensure cross-origin form submissions include a `Content-Type` header, or add the origin to `csrf.trustedOrigins`.

    CORS for static assets in development is handled by Vite[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Security-CORS-for-static-assets-in-development-is-handled-by-Vite)

    SvelteKit no longer sets `access-control-allow-origin: *` on every static asset request in development. CORS is now delegated to Vite’s built-in middleware. If you rely on cross-origin access to static assets in dev, configure it in your Vite config:

    vite.config

    export default defineConfig(alias) defineConfig(config: UserConfig): UserConfig (+5 overloads)
    import defineConfigType helper to make it easier to use vite.config.ts
    accepts a direct 
    {@link 
    UserConfig
    }
     object, or a function that returns it.
    The function receives a 
    {@link 
    ConfigEnv
    }
     object.
    ({
    	server(property) UserConfig.server?: ServerOptions$1 | undefinedServer specific options, e.g. host, port, https...
    : {
    		cors(property) CommonServerOptions.cors?: boolean | CorsOptions | undefinedConfigure CORS for the dev server.
    Uses https://github.com/expressjs/cors.
    When enabling this option, we recommend setting a specific value
    rather than true to avoid exposing the source code to untrusted origins.
    Set to true to allow all methods from any origin, or configure separately
    using an object.
    { origin: /^https?://(?:(?:[^:]+.)?localhost|127.0.0.1|[::1])(?::\d+)?$/ }
    : { origin(property) CorsOptions.origin?: CorsOrigin | ((origin: string | undefined, cb: (err: Error, origins: CorsOrigin) => void) => void) | undefinedConfigures the Access-Control-Allow-Origin CORS header.
    We recommend setting a specific value rather than
    true to avoid exposing the source code to untrusted origins.
    : '*' }
    	}
    });

    Cookies[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Cookies)

    Updated to cookie v2[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Cookies-Updated-to-cookie-v2)

    SvelteKit now uses cookie v2, which involves certain changes:

  • cookie names can only contain ASCII characters. Non-ASCII characters (including Latin-1 Supplement characters like `á`) are rejected
  • the `CookieSerializeOptions` type has been renamed to `SerializeOptions`
  • the `CookieParseOptions` type has been renamed to `ParseOptions`
  • Paths default to '/'[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Cookies-Paths-default-to-'-')

    When setting a cookie without an explicit `path` (which was previously forbidden), the path defaults to `'/'` rather than the current request path, meaning the cookie applies to the entire site. This matches what most developers expect. You can pass an explicit `path` if necessary:

    cookiesconst cookies: Cookies.set(property) Cookies.set: (name: string, value: string, opts?: SerializeOptions) => voidSets a cookie. This will add a set-cookie header to the response, but also make the cookie available via cookies.get or cookies.getAll during the current request.
    The httpOnly is true by default, as is secure, except during development, when it defaults to false. These must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP.
    The path option is '/' by default. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children.
    name the name of the cookie
    value the cookie value
    opts the options passed to cookie.stringifySetCookie with the SvelteKit defaults described above. See documentation here
    (nameconst name: string, valueconst value: string, { path(property) path?: string | undefinedSpecifies the value for the Path Set-Cookie attribute.
    When no path is set, the path is considered the "default path".
    : '/some/path' });

    Error handling[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Error-handling)

    App.Error always includes status[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Error-handling-App.Error-always-includes-status)

    An `App.Error` object, such as the `error` prop of an `+error.svelte` component, has a `status` property reflecting the HTTP status of the error that caused it (e.g. 404 for a Not Found error, or 500 for a generic internal error), in addition to `message` and whatever properties you define in your app.d.ts file.

    error(...) arguments changed[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Error-handling-error%28%29-arguments-changed)

    Previously, the second argument to error(...) could be either the `message` as a `string`, or an object containing `message` alongside any additional properties defined in `app.d.ts` (such as a tracking `code`).

    Now, the second argument must always be a `string`. If there are additional properties, they must be passed as a third argument.

    handleValidationError is removed[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Error-handling-handleValidationError-is-removed)

    Validation errors are now passed to handleError with `kind: 'validation'`. The generic `{ status, message }` `error` object is safe to return, while the validation `issues` are provided separately for logging or custom handling.

    handleError receives all errors[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Error-handling-handleError-receives-all-errors)

    In SvelteKit 2, `handleError` was not called in the case of expected errors, which is to say those created with the error(...) helper. In SvelteKit 3, all errors are passed to `handleError`. See the docs for more information.

    handleError can influence the status code[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Error-handling-handleError-can-influence-the-status-code)

    If you need to control the HTTP status code used to render a page in the case of an error, you can do so by returning a `status` property from `handleError` alongside any other required properties of `App.Error`.

    Rendering errors are now handled[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Error-handling-Rendering-errors-are-now-handled)

    Errors thrown during rendering are now always routed through `handleError` and then passed to the nearest error boundary. Error boundaries are automatically created for each of your `+error.svelte` components.

    If you have an async `handleError` hook in `hooks.client.ts`, enable `compilerOptions.experimental.async` in the `sveltekit(...)` plugin options of your Vite config so it can be awaited during rendering.

    Form action responses use the fail status code[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Error-handling-Form-action-responses-use-the-fail-status-code)

    Enhanced form action responses now use the HTTP status code passed to `fail(...)` instead of always returning `200`. If you inspect status codes on enhanced form submissions (for example in a `use:enhance` callback or in tests), they now reflect the value passed to `fail`.

    Sourcemaps are applied[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Error-handling-Sourcemaps-are-applied)

    Sourcemaps are generated by default, and applied to stack traces when errors occur. For this to work in production (as opposed to `vite preview`), adapters need to avoid any destructive changes (such as rebundling without generating additional — and correct — sourcemaps). For first-party adapters this is a work in progress.

    Params[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Params)

    Param matchers are no longer files inside the `src/params` directory. Declare all matchers in a single `src/params.ts` (or `src/params.js`) file using the `defineParams` helper. A matcher can be a function that returns a parsed value (or `undefined`, if the param does not match), or a Standard Schema.

    src/params

    import { defineParams(alias) function defineParams<T extends Record<string, ParamDefinition>>(definitions: T): DefinedParams<T>
    import defineParamsDefine parameter matchers for your app.
    reference } from '@sveltejs/kit/params';
    import * as vimport v from 'valibot';

    export const paramsconst params: DefinedParams<{ integer: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.ToNumberAction<string, undefined>]>; fruit: (param: string) => "apple" | "orange" | undefined; }> = defineParams(alias) defineParams<{ integer: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.ToNumberAction<string, undefined>]>; fruit: (param: string) => "apple" | "orange" | undefined; }>(definitions: { integer: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.ToNumberAction<string, undefined>]>; fruit: (param: string) => "apple" | "orange" | undefined; }): DefinedParams<{ integer: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.ToNumberAction<string, undefined>]>; fruit: (param: string) => "apple" | "orange" | undefined; }> import defineParamsDefine parameter matchers for your app. reference({ // schema variant integer(property) integer: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.ToNumberAction<string, undefined>]>: vimport v.pipe(alias) pipe<v.StringSchema<undefined>, v.ToNumberAction<string, undefined>>(schema: v.StringSchema<undefined>, item1: v.ToNumberAction<string, undefined> | v.PipeAction<string, number, v.ToNumberIssue<string>>): v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.ToNumberAction<string, undefined>]> (+20 overloads) export pipeAdds a pipeline to a schema, that can validate and transform its input. schema The root schema. item1 The first pipe item. A schema with a pipeline. (vimport v.string(alias) string(): v.StringSchema<undefined> (+1 overload) export stringCreates a string schema. A string schema. (), vimport v.toNumber(alias) toNumber<string>(): v.ToNumberAction<string, undefined> (+1 overload) export toNumberCreates a to number transformation action. A to number action. ()),

    // function variant fruit(property) fruit: (param: string) => "apple" | "orange" | undefined: (param(parameter) param: string) => { if (param(parameter) param: string === 'apple' || param(parameter) param: string === 'orange') { return param(parameter) param: "apple" | "orange"; } } });

    Observability[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Observability)

    Server-side instrumentation now happens automatically if a `src/instrumentation.server.js` file exists.

    To opt into OpenTelemetry tracing, add `tracing.server` configuration:

    vite.config

    export default defineConfig(alias) defineConfig(config: UserConfig): UserConfig (+5 overloads)
    import defineConfigType helper to make it easier to use vite.config.ts
    accepts a direct 
    {@link 
    UserConfig
    }
     object, or a function that returns it.
    The function receives a 
    {@link 
    ConfigEnv
    }
     object.
    ({
    	plugins(property) UserConfig.plugins?: PluginOption[] | undefinedArray of vite plugins to use.
    : [
    		sveltekit(alias) sveltekit(config?: Config): Promise<Plugin[]>
    import sveltekitThe SvelteKit Vite plugin, which must be added to your vite.config.js file along with your project's configuration:
    /// file: vite.config.js
    import adapter from '@sveltejs/adapter-auto';
    import { sveltekit } from '@sveltejs/kit/vite';
    import { defineConfig } from 'vite';

    export default defineConfig({ plugins: [ sveltekit({ adapter: adapter(), compilerOptions: { experimental: { async: true } }, experimental: { remoteFunctions: true } }) ] });As well as SvelteKit, the plugin options are used by other tooling that integrates with Svelte such as editor extensions. Any options that don't belong to SvelteKit are passed through to vite-plugin-svelte, so you can set options like inspector here too. The experimental namespace is shared — SvelteKit reads its own flags and forwards the rest.

    [!LEGACY] Prior to SvelteKit 3, config lived in a svelte.config.js file, which is no longer supported. The ability to configure SvelteKit via vite.config.js was added in version 2.62.

    reference({ tracing(property) Config.tracing?: { server?: boolean; } | undefinedOptions for enabling OpenTelemetry tracing for SvelteKit operations. { server: false } : { server(property) server?: boolean | undefinedEnables server-side OpenTelemetry span emission for SvelteKit operations including the handle hook, load functions, form actions, and remote functions. Tracing — and more significantly, observability instrumentation — can have a nontrivial overhead, so consider whether you really need it, or if it might be more appropriate to turn it on in development and preview environments only. false : true } }) ] });

    Adapters[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Adapters)

    All first-party adapters now require SvelteKit 3, alongside these adapter-specific changes:

    adapter-cloudflare[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Adapters-adapter-cloudflare)

    Cloudflare-specific APIs are no longer available on `platform`. Instead, find them where you would expect on a Cloudflare worker:

  • `env`, `ctx.waitUntil`, and other `ctx` properties should be imported from `cloudflare:workers`:
  • import { envimport env, waitUntilimport waitUntil } from 'cloudflare:workers';

    const valueconst value: any = await envimport env.KVany.getany('key');

  • `cf` is now a property of the `Request` object:
  • src/routes/cf/+server

    export async function GETfunction GET({ request }: {
        request: any;
    }): Promise<void>({ request(parameter) request: any }) {
    	const { countryconst country: any } = request(parameter) request: any.cfany;
    }

  • `caches` is now a global variable:
  • src/routes/cache/+server

    const myCacheconst myCache: Cache = await cachesvar caches: CacheStorageAvailable only in secure contexts.
    MDN Reference
    .open(method) CacheStorage.open(cacheName: string): Promise<Cache>The open() method of the CacheStorage interface returns a Promise that resolves to the Cache object matching the cacheName.
    MDN Reference
    ('foo');
    await myCacheconst myCache: Cache.match(method) Cache.match(request: RequestInfo | URL, options?: CacheQueryOptions): Promise<Response | undefined>The match() method of the Cache interface returns a Promise that resolves to the Response associated with the first matching request in the Cache object. If no match is found, the Promise resolves to undefined.
    MDN Reference
    (requestlet request: Request);

  • To make these types available to your app, install wrangler and run wrangler types.
  • minimum `wrangler` is now `^4.67.0`
  • `@cloudflare/workers-types` upgraded
  • adapter-node[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Adapters-adapter-node)

  • bundling now happens with `rolldown`
  • the `ORIGIN` environment variable is removed (set `paths.origin` in your Vite config instead)
  • static assets are served from a list recorded at build time; files added to the output directory afterwards are not served, and replaced ones keep their old size and `ETag` (use environment variables for runtime configuration)
  • `ETag`s for static assets are content hashes, and `Last-Modified` is no longer sent
  • only `GET` and `HEAD` requests are served static assets; other methods result in a 405 response
  • adapter-netlify[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Adapters-adapter-netlify)

  • output now conforms to the stable Netlify Frameworks API
  • deploying/previewing with the Netlify CLI requires `v17.31.0` or later (`npm i -g netlify-cli@latest`)
  • edge function build target is `es2022`
  • the publish directory is now an adapter option rather than being read from the `netlify.toml` file
  • adapter-vercel[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Adapters-adapter-vercel)

  • the `edge` runtime is no longer supported
  • Adapter API changes[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Adapters-Adapter-API-changes)

    For adapter authors, there are some additional changes:

  • adapters can augment the Vite config with additional plugins
  • `builder.config.kit` no longer exists — the configuration now lives at the top level
  • `builder.createEntries` has been removed — use `builder.writeClient`, `builder.writeServer` and `builder.writePrerendered` directly
  • `builder.compress` returns a list of compressed files
  • `builder.mkdirp` and `builder.rimraf` are deprecated in favour of `node:fs` methods
  • `builder.generateManifest` has been removed — use `builder.generateServerInstance` to replace it, and `builder.manifest` to access the manifest
  • the `Server` class exported from the server output is deprecated — use the `server` object written by `builder.generateServerInstance`

#### Server instrumentation[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Adapters-Adapter-API-changes-Server-instrumentation)

Adapters that use `builder.instrument` must now generate an environment initializer before any bundling step, include the returned module as an entrypoint in that step, and pass its final path to `builder.instrument`:

const initializerconst initializer: string = builderconst builder: Builder.createInstrumentationInitializer(property) Builder.createInstrumentationInitializer: (options: {
    outputDirectory: string;
    environment?: string;
    serverDirectory?: string;
}) => stringGenerate an initializer that populates $env/dynamic/private before server instrumentation
runs. Include the returned module in any subsequent bundling or tracing step.
options an object containing the following properties:
options.outputDirectory the directory in which to create the initializer.
options.environment the contents of a module whose default export contains the platform's environment variables. If omitted, process.env is used.
options.serverDirectory the directory containing the server build output. Defaults to getServerDirectory().
the filesystem path to the generated initializer.
3.0.0
({ outputDirectory(property) outputDirectory: string: temporary_directoryconst temporary_directory: string });

// Include `initializer` in any bundling or file tracing here

builderconst builder: Builder.instrument(property) Builder.instrument: (args: { entrypoint: string; instrumentation: string; start?: string; initializer: string; module?: { exports: string[]; } | { generateText: (args: { instrumentation: string; start: string; initializer: string; }) => string; }; }) => voidInstrument entrypoint with instrumentation. Renames entrypoint to start and creates a new module at entrypoint which imports instrumentation and then dynamically imports start. This allows the module hooks necessary for instrumentation libraries to be loaded prior to any application code. initializer is a module generated by createInstrumentationInitializer. It must be included in any bundling or tracing step before calling this method. Caveats:

"Live exports" will not work. If your adapter uses live exports, your users will need to manually import the server instrumentation on startup. If tla is false, OTEL auto-instrumentation may not work properly. Use it if your environment supports it. Use hasServerInstrumentationFile to check if the user has a server instrumentation file; if they don't, you shouldn't do this.

options an object containing the following properties: options.entrypoint the path to the entrypoint to trace. options.instrumentation the path to the instrumentation file. options.start the name of the start file. This is what entrypoint will be renamed to. options.initializer the filesystem path to the bundled or copied instrumentation initializer. options.module configuration for the resulting entrypoint module. options.module.generateText a function that receives the relative paths to the initializer, instrumentation and start files, and generates the text of the module to be traced. It must import initializer before instrumentation, and dynamically import start after instrumentation has run. If not provided, the default implementation will be used, which uses top-level await. 3.0.0 ({ entrypoint(property) entrypoint: string, instrumentation(property) instrumentation: string, initializer(property) initializer: string });

For runtimes that do not expose environment variables through `process.env`, pass the contents of a module whose default export is the platform environment:

const initializerconst initializer: string = builderconst builder: Builder.createInstrumentationInitializer(property) Builder.createInstrumentationInitializer: (options: {
    outputDirectory: string;
    environment?: string;
    serverDirectory?: string;
}) => stringGenerate an initializer that populates $env/dynamic/private before server instrumentation
runs. Include the returned module in any subsequent bundling or tracing step.
options an object containing the following properties:
options.outputDirectory the directory in which to create the initializer.
options.environment the contents of a module whose default export contains the platform's environment variables. If omitted, process.env is used.
options.serverDirectory the directory containing the server build output. Defaults to getServerDirectory().
the filesystem path to the generated initializer.
3.0.0
({
	outputDirectory(property) outputDirectory: string: temporary_directoryconst temporary_directory: string,
	environment(property) environment?: string | undefined: `import { env } from 'cloudflare:workers';\nexport default env;`
});

Responses[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Responses)

204 responses return no content[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Responses-204-responses-return-no-content)

Returning a `204` (or any empty `2xx`) response from a `+server.js` handler now results in a response with no body, per the HTTP spec, rather than a SvelteKit envelope. Code that consumed the body of such responses needs to handle the empty body.

resolve always returns a Promise[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Responses-resolve-always-returns-a-Promise)

The `resolve` function passed to handle is now typed to always return a `Promise<Response>` rather than `MaybePromise<Response>`.

Server-only modules[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Server-only-modules)

Files[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Server-only-modules-Files)

Server-only modules are now designated by a filename with a `server` segment, rather than a `.server.` infix — in other words, `stuff.server.ts`, `stuff.server.test.ts` and `server.ts` are all treated as server-only modules, whereas `server.ts` previously was not.

Directories[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Server-only-modules-Directories)

Previously, any module inside `src/lib/server` was treated as server-only. This treatment now applies to any `server` directory in the project with the exception of `src/routes` and your `static` directory.

Remote functions[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Remote-functions)

Remote functions are still considered experimental — opt in via the `experimental.remoteFunctions` flag alongside `compilerOptions.experimental.async`:

vite.config

export default defineConfig(alias) defineConfig(config: UserConfig): UserConfig (+5 overloads)
import defineConfigType helper to make it easier to use vite.config.ts
accepts a direct 
{@link 
UserConfig
}
 object, or a function that returns it.
The function receives a 
{@link 
ConfigEnv
}
 object.
({
	plugins(property) UserConfig.plugins?: PluginOption[] | undefinedArray of vite plugins to use.
: [
		sveltekit(alias) sveltekit(config?: Config): Promise<Plugin[]>
import sveltekitThe SvelteKit Vite plugin, which must be added to your vite.config.js file along with your project's configuration:
/// file: vite.config.js
import adapter from '@sveltejs/adapter-auto';
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';

export default defineConfig({ plugins: [ sveltekit({ adapter: adapter(), compilerOptions: { experimental: { async: true } }, experimental: { remoteFunctions: true } }) ] });As well as SvelteKit, the plugin options are used by other tooling that integrates with Svelte such as editor extensions. Any options that don't belong to SvelteKit are passed through to vite-plugin-svelte, so you can set options like inspector here too. The experimental namespace is shared — SvelteKit reads its own flags and forwards the rest.

[!LEGACY] Prior to SvelteKit 3, config lived in a svelte.config.js file, which is no longer supported. The ability to configure SvelteKit via vite.config.js was added in version 2.62.

reference({ compilerOptions(property) compilerOptions?: Omit<CompileOptions, "filename" | "format" | "generate"> | undefinedThe options to be passed to the Svelte compiler. A few options are set by default, including dev and css. However, some options are non-configurable, like filename, format, generate, and cssHash (in dev). https://svelte.dev/docs/svelte/svelte-compiler#CompileOptions : { experimental(property) experimental?: { async?: boolean; } | undefinedExperimental options 5.36 : { async(property) async?: boolean | undefinedAllow await keyword in deriveds, template expressions, and the top level of components 5.36 : true } }, experimental(property) Config.experimental?: (ExperimentalOptions & { remoteFunctions?: boolean; forkPreloads?: boolean; }) | undefinedExperimental features. Here be dragons. These are not subject to semantic versioning, so breaking changes or removal can happen in any release. : { remoteFunctions(property) remoteFunctions?: boolean | undefinedWhether to enable the experimental remote functions feature. This feature is not yet stable and may be changed or removed at any time. false : true } }) ] });

Remote module filenames[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Remote-functions-Remote-module-filenames)

As with server-only modules, a `remote` segment in a filename designates a remote module — `stuff.remote.ts`, `stuff.remote.test.ts` and `remote.ts` are all remote modules.

If `experimental.remoteFunctions` is not enabled, the existence of these files will cause an error.

event.url, event.params, and event.route cannot be accessed inside queries[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Remote-functions-event.url-event.params-and-event.route-cannot-be-accessed-inside-queries)

Accessing `event.url`, `event.params`, or `event.route` inside a remote `query` function now throws an error. These properties are not meaningful in the context of a remote function (which can be called from anywhere). Pass any values you need explicitly as arguments to the function.

Errors are typed as App.Error | undefined[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Remote-functions-Errors-are-typed-as-App.Error-undefined)

The `error` property on remote function resources (queries, live queries, forms, prerender functions) is now typed as `App.Error | undefined` rather than `any`, as the error is always transformed by `handleError`.

Form submissions require field.as(...)[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Remote-functions-Form-submissions-require-field.as%28%29)

A form control must use attributes from a field associated with the current `form` object:

<input {...myform.fields.message.as('text')}>

Manually specifying a name (`<input name="message">`) will cause the submission to be rejected.

Miscellaneous[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Miscellaneous)

Links to the current page cause a refresh[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Miscellaneous-Links-to-the-current-page-cause-a-refresh)

If the user clicks a link that points to the current location, SvelteKit will `refreshAll()` instead of doing nothing.

data-sveltekit-\* uses false instead of 'off'[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Miscellaneous-data-sveltekit-uses-false-instead-of-'off')

The `'off'` value for `data-sveltekit-*` link attributes has been removed in favour of `false`.

<a href="..." data-sveltekit-preload-data="off">
<a href="..." data-sveltekit-preload-data="false">

External redirects must be opted into[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Miscellaneous-External-redirects-must-be-opted-into)

To redirect to an external URL you must now pass an `external` option — either `true` to allow any external URL (except `javascript:` URLs, which remain blocked), or an array of allowed origins (which can include `javascript:` URLs).

redirect(alias) redirect(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number), location: string | URL, options?: {
    external?: boolean | string[];
}): never
import redirectRedirect a request. When called during request handling, SvelteKit will return a redirect response.
Make sure you're not catching the thrown redirect, which would prevent SvelteKit from handling it.
Most common status codes:

303 See Other: redirect as a GET request (often used after a form POST request) 307 Temporary Redirect: redirect will keep the request method 308 Permanent Redirect: redirect will keep the request method, SEO will be transferred to the new page

See all redirect status codes status The HTTP status code. Must be in the range 300-308. location The location to redirect to. options To redirect to an external URL, you must pass { external: true } to allow any external URL except javascript: URLs, or { external: [...] } with an allowlist of permitted origins. {import('./public.js').Redirect} This error instructs SvelteKit to redirect to the specified location. {Error} If the provided status is invalid, the location cannot be used as a header value, or the location is an external URL without permission. reference(307, 'https://example.com', { external(property) external?: boolean | string[] | undefined: true });

Universal config takes precedence over server config[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Miscellaneous-Universal-config-takes-precedence-over-server-config)

Route `config` exported from a universal `+page.js` or `+layout.js` now takes precedence over `config` exported from the corresponding `+page.server.js` or `+layout.server.js`, matching how other page options are resolved. If you export `config` from both, move the canonical export to the universal file or consolidate them.

Service worker registrations use type: 'module'[](https://next.svelte.dev/docs/kit/migrating-to-sveltekit-3#Miscellaneous-Service-worker-registrations-use-type:-'module')

Module service workers are now widely supported. As such, SvelteKit will bundle and register your service worker as a module, rather than as a script.

Edit this page on GitHub llms.txt

previous next

Migrating to SvelteKit v2 Migrating from Sapper

next.svelte.dev · 147 links

Comments (0)

No comments yet