diff --git a/documentation/docs/60-appendix/35-migrating-to-sveltekit-3.md b/documentation/docs/60-appendix/35-migrating-to-sveltekit-3.md
new file mode 100644
index 000000000000..0c376629b55e
--- /dev/null
+++ b/documentation/docs/60-appendix/35-migrating-to-sveltekit-3.md
@@ -0,0 +1,401 @@
+---
+title: Migrating to SvelteKit v3
+---
+
+SvelteKit 3 contains many small breaking changes, most of which you probably won't notice. The minimum dependency versions have been raised, a number of long-deprecated APIs have been removed, and configuration has moved from `svelte.config.js` into the Vite plugin. This guide lists every breaking change and how to migrate.
+
+We highly recommend upgrading to the most recent 2.x version before upgrading to 3.0, and take advantage of its deprecating warnings before upgrading.
+
+## Updated dependency requirements
+
+SvelteKit 3 raises the minimum supported versions of its dependencies:
+
+- **Node** 22 or newer
+- **TypeScript** 6 or newer
+- **Svelte** 5.56.4 or newer
+- **Vite** 8.0.12 or newer (the first Vite 8 release bundling stable `rolldown` 1.0.0)
+- **`@sveltejs/vite-plugin-svelte`** v7 or newer
+
+Update the versions in your `package.json` and run your package manager's install command.
+
+## Configuration is now passed to the Vite plugin
+
+`svelte.config.js` is no longer supported. SvelteKit configuration must now be passed to the `sveltekit()` Vite plugin in `vite.config.js`, and the `kit` prefix is dropped — options that used to live under `config.kit.*` are now top-level options of the plugin. They are places alongside other options such as `compilerOptions` for Svelte.
+
+To migrate, move the contents of `svelte.config.js` into the `sveltekit()` plugin call and delete `svelte.config.js`:
+
+```js
+// --- svelte.config.js (deleted) ---
+import adapterVercel from '@sveltejs/adapter-vercel';
+
+/** @type {import('@sveltejs/kit').Config} */
+const config = {
+ compilerOptions: { experimental: { async: true }},
+ kit: {
+ adapter: adapterVercel()
+ }
+};
+export default config;
+
+// --- vite.config.js ---
+import { sveltekit } from '@sveltejs/kit/vite';
+import adapterVercel from '@sveltejs/adapter-vercel';
+import { defineConfig } from 'vite';
+
+export default defineConfig({
+ plugins: [
+ sveltekit(+++{
+ compilerOptions: { experimental: { async: true }},
+ adapter: adapterVercel()
+ }+++)
+ ]
+});
+```
+
+## `$lib` is replaced by `#lib`
+
+The `$lib` alias is no longer generated automatically by SvelteKit. It is replaced by a `#lib` alias that you declare in the [`imports`](https://nodejs.org/api/packages.html#subpath-imports) field of your `package.json`, leveraging Node's built-in subpath imports (which Vite and TypeScript resolve natively).
+
+```json
+{
+ "imports": {
+ "#lib": "./src/lib/index.js",
+ "#lib/*": "./src/lib/*"
+ }
+}
+```
+
+```js
+---import { foo } from '$lib/foo.js';---
++++import { foo } from '#lib/foo.js';+++
+```
+
+Consequently, the `kit.files.lib` configuration option has also been removed — `src/lib` is no longer special-cased by SvelteKit.
+
+To migrate, find-and-replace `$lib` with `#lib` across your codebase, add the `imports` entry above to `package.json`, and remove any `files.lib` config.
+
+## Param matchers live in a single `params.js/ts` file
+
+Param matchers are no longer files inside the `src/params/` directory. Declare all matchers in a single `src/params.js` (or `src/params.ts`) file using the `defineParams` helper. A matcher can be a function that returns a parsed value (or `undefined` to not match), or a [Standard Schema](https://standardschema.dev).
+
+```js
+// --- src/params/integer.js (deleted) ---
+export function match(param) {
+ return /^\d+$/.test(param);
+}
+
+// --- src/params.js ---
+import { defineParams } from '@sveltejs/kit';
+import * as v from 'valibot';
+
+export const params = defineParams({
+ integer: v.pipe(v.string(), v.toNumber()), // schema variant
+ fruit: (param) => (param === 'apple' || param === 'orange' ? param : undefined) // function variant
+});
+```
+
+To migrate, consolidate every `src/params/*.js` file into `src/params.js`/`params.ts`. When a matcher returns a parsed value (or a Standard Schema is used), the route `params` are now typed with the output type.
+
+## `$app/stores` has been removed
+
+`$app/stores` (the `$page`, `$navigating`, and `$updated` stores) has been removed. Use `$app/state` instead, which is based on the Svelte 5 runes API and provides fine-grained, non-store values.
+
+```svelte
+
+
+---{$page.data}---
++++{page.data}+++
+```
+
+Replace `$app/stores` imports with `$app/state` and remove the `$` prefix when reading values.
+
+## `base`, `assets`, and `resolveRoute` removed from `$app/paths`
+
+The deprecated `base`, `assets`, and `resolveRoute` exports have been removed from `$app/paths`. Use the replacements that were introduced in SvelteKit 2.26:
+
+- `resolveRoute(id, params)` → `resolve(id, params)` (also accepts a plain pathname)
+- `assets + '/foo.png'` → `asset('foo.png')`
+
+```js
+---import { base, resolveRoute } from '$app/paths';---
++++import { resolve } from '$app/paths';+++
+
+---const path = base + resolveRoute('/blog/[slug]', { slug });---
++++const path = resolve('/blog/[slug]', { slug });+++
+```
+
+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.
+
+## Changes to shallow routing
+
+`pushState/replaceState` are deprecated in favor of `goto`:
+
+```js
+import { goto, pushState, replaceState } from '$app/navigation';
+
+---pushState('/foo', state);---
++++goto('/foo', { shallow: true, state });+++
+
+---replaceState('/bar', state);---
++++goto('/bar', { shallow: true, replace: true, state });+++
+```
+
+Shallow routing now triggers navigation hooks (`before/after/onNavigate`). You can filter them out by checking the `shallow` property of the object passed to those navigation hooks.
+
+## External redirects must be opted into
+
+`redirect(...)` to an external URL is now forbidden by default. To redirect to an external destination, pass an `external` option — either `true` to allow any external URL (except `javascript:` URLs, which are always blocked), or an array of allowed origins (through which you can allow `javascript:` URLs).
+
+```js
+import { redirect } from '@sveltejs/kit';
+
+---redirect(307, 'https://example.com');---
++++redirect(307, 'https://example.com', { external: true });+++
+```
+
+## `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](shallow-routing). The `goto(..., { invalidateAll })` option is likewise deprecated in favour of `refreshAll`.
+
+```js
+import { +++refreshAll+++ } from '$app/navigation';
+
+---await invalidateAll();---
++++await refreshAll();+++
+```
+
+Calling `invalidate(All)` during an in-flight navigation no longer aborts that navigation.
+
+## `handleError` can influence the status code
+
+`handleError` can now return a `status` property to override the response status code, and `App.Error` now always includes a `status: number` property. `status` is a reserved key on the returned object and is set by the framework — if your `App.Error` type previously declared its own `status` field for other purposes, rename it.
+
+```js
+export async function handleError({ error, event, status, message }) {
+ return {
+ +++status: 418,+++
+ message: 'something went wrong'
+ };
+}
+```
+
+## Rendering errors now handled
+
+The `experimental.handleRenderingErrors` flag has been removed; errors thrown during rendering are now always routed through `handleError` and then passed to the nearest error boundary, which can also be a `+error.svelte`. Remove the flag from your config. If your client `handleError` hook is `async`, enable `compilerOptions.experimental.async` in the SvelteKit plugin options so it can be awaited during rendering.
+
+## Tracing is no longer experimental
+
+Server-side tracing is no longer configured under `experimental.tracing`/`experimental.instrumentation`. `src/instrumentation.server.js` is now included in the build automatically when it exists, and tracing is configured at the top level via `tracing.server`.
+
+```js
+experimental: {
+ ---tracing: { server: true }---
+}
++++tracing: {
+ server: true
+}+++
+```
+
+## Adapters
+
+All first-party adapters now require SvelteKit 3. The deprecated `builder.createEntries` method has also been removed from the `Builder` object passed to adapters — use `builder.writeClient`/`builder.writeServer`/`builder.writePrerendered` directly.
+
+Adapter-specific breaking changes:
+
+- **`adapter-cloudflare`** — minimum `wrangler` is now `^4.67.0`; `@cloudflare/workers-types` upgraded; `platform.context` removed in favour of `platform.ctx`.
+- **`adapter-node`** — bundled with `rolldown`; the `ORIGIN` environment variable is removed (use `paths.origin` in config).
+- **`adapter-netlify`** — output now conforms to the stable [Netlify Frameworks API](https://docs.netlify.com/build/frameworks/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`.
+- **`adapter-vercel`** — edge function build target is now `es2022`; edge functions are bundled with `rolldown`.
+
+## Routing and project structure
+
+### Consistent special filename patterns
+
+Special filenames are now matched on a _segment_ basis rather than only via the `*.server.*` / `*.remote.*` infix. A `server` segment anywhere in a file's path makes it server-only, and a `remote` segment anywhere in the path makes it a remote module.
+
+This means `*.remote.*.ts` now works the same way `*.server.*.ts` always has, and bare files named `server.ts` or `remote.ts` are now treated as special (previously they were not). If you have a file named `server.ts` or `remote.ts` that should _not_ be treated as server-only/remote, rename it.
+
+### Server-only directories
+
+Any directory named `server` in the path is now treated as server-only everywhere in the project, not just `$lib/server`; with the exception of `src/routes` and the assets directory.
+
+If you have a `server/` directory that is meant to be importable from client code, rename it. Otherwise this change simply expands protection against accidentally importing server code into the browser.
+
+### Universal `config` takes precedence over server `config`
+
+`config` exported from a universal `+page.js`/`+layout.js` now takes precedence over `config` exported from the corresponding `+page.server.js`/`+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.
+
+## Modules and imports
+
+### `@sveltejs/kit/node/polyfills` has been removed
+
+The `@sveltejs/kit/node/polyfills` module (and the Node global shims in `adapter-node`/`adapter-netlify`) have been removed. They were only needed for older Node versions, which are no longer supported. Remove any `import '@sveltejs/kit/node/polyfills'` statements from your custom server code.
+
+### `defineEnvVars` moved to `@sveltejs/kit/env`
+
+`defineEnvVars` is no longer exported from `@sveltejs/kit/hooks`. Import it from `@sveltejs/kit/env` instead.
+
+```js
+---import { defineEnvVars } from '@sveltejs/kit/hooks';---
++++import { defineEnvVars } from '@sveltejs/kit/env';+++
+```
+
+### `data-sveltekit-*` uses `false` instead of `'off'`
+
+The `'off'` value for `data-sveltekit-*` link attributes has been removed in favour of `false`.
+
+```svelte
+------
+++++++
+```
+
+### `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.
+
+## Navigation and data loading
+
+### `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. Ensure `goto` targets correspond to real routes. To navigate to an external URL, use `window.location.href = url`.
+
+### `delta` only exists for `popstate` navigations
+
+The `delta` property on navigation events (`beforeNavigate`/`afterNavigate`/`onNavigate`) now only exists for `popstate` navigations (back/forward). It is `undefined` for all other navigation types.
+
+### `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:
+
+```js
+const result = await preloadData(url);
+
+if (result.type === 'loaded') {
+ // ...
+} +++else if (result.type === 'error') {
+ // do something in case of an error
+}+++
+```
+
+### `page.url` is now immutable on the type level
+
+`page.url` (from `$app/state`) 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:
+
+```js
+const url = +++new URL(+++page.url.href+++);+++
+url.searchParams.set('q', 'svelte');
+```
+
+## Cookies
+
+### Cookie names must be ASCII
+
+SvelteKit now uses `cookie` v2, which requires cookie names to contain only ASCII characters. Non-ASCII characters (including Latin-1 Supplement characters like `á`) are rejected. Rename any non-ASCII cookie names to ASCII equivalents. If you depended on the `CookieSerializeOptions`/`CookieParseOptions` types, import `SerializeOptions`/`ParseOptions` from `cookie` instead.
+
+### The cookie `path` option defaults to `'/'`
+
+When setting a cookie without an explicit `path`, the path now defaults to `'/'` (the whole site) rather than the current request path. This matches what most developers expect. If you relied on the previous implicit behaviour, pass an explicit `path`:
+
+```js
+cookies.set(name, value, +++{ path: '/some/path' }+++);
+```
+
+## Responses and error handling
+
+### 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.
+
+### 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`.
+
+### `getRequest` and `setResponse` are synchronous
+
+The `getRequest` and `setResponse` helpers from `@sveltejs/kit/node` are now synchronous and no longer return Promises. Remove `await` from calls in custom Node servers or adapters:
+
+```js
+---const request = await getRequest(req);---
+---await setResponse(res, response);---
++++const request = getRequest(req);+++
++++setResponse(res, response);+++
+```
+
+### `handle`'s `resolve` always returns a `Promise`
+
+The `resolve` function passed to `handle` is now typed to always return a `Promise` rather than `MaybePromise`. If you wrap `resolve` in a custom function that returns a bare `Response`, make the wrapper `async` or wrap the return value in `Promise.resolve(...)`.
+
+### `form.error` is 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`. Because all errors are transformed through `handleError` before surfacing, the value is always `App.Error`-shaped.
+
+## 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`, allowlist trusted cross-origin hosts with `csrf.trustedOrigins`.
+
+```js
+csrf: {
+ ---checkOrigin: false---
+ +++trustedOrigins: ['https://trusted-site.com']+++
+}
+```
+
+### Cross-origin form submissions require a `Content-Type` header
+
+Cross-origin form submissions that omit a `Content-Type` header are now rejected as CSRF, where previously they were allowed through ([#16347](https://github.com/sveltejs/kit/pull/16347)). 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
+
+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:
+
+```js
+export default defineConfig({
+ server: {
++++ cors: { origin: '*' }+++
+ }
+});
+```
+
+## Remote functions
+
+### Remote functions require an opt-in
+
+Files matching `*.remote.ts`/`*.remote.js` now error during development and builds unless `experimental.remoteFunctions` is enabled. As such they are now reserved for 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.
+
+## Removed and reorganised configuration options
+
+### `output.preloadStrategy` removed
+
+The `preloadStrategy` option has been removed. `modulepreload` is always used. Remove `output.preloadStrategy` from your config.
+
+### `prerender.origin` replaced by `paths.origin`
+
+`prerender.origin` has been removed in favour of `paths.origin`, which is also used as the trusted self-origin for CSRF checks on form submissions and remote function calls. The `adapter-node` `ORIGIN` environment variable has also been removed — set `paths.origin` in your config instead.
+
+```js
+prerender: {
+ ---origin: 'https://example.com'---
+},
++++paths: {
+ origin: 'https://example.com'
+},+++
+```
+
+### `output.linkHeaderPreload`
+
+Preloading via the `Link` response header is no longer the default (it broke common self-hosted reverse proxies). Dynamically rendered pages now preload via `` elements in the HTML instead. If you relied on the `Link` header, opt back in:
+
+```js
+output: {
++++ linkHeaderPreload: true+++
+}
+```