This guide moves a SvelteKit ↗︎ application from Vercel to Cloudflare Workers. You replace @sveltejs/adapter-vercel or @sveltejs/adapter-auto with @sveltejs/adapter-cloudflare ↗︎.
The adapter version depends on your SvelteKit major version:
| SvelteKit version | Adapter | Configured in | Bindings |
|---|---|---|---|
| Earlier than 2 | None. Upgrade to SvelteKit 2 first. | — | — |
| 2 | @sveltejs/adapter-cloudflare@^7 |
svelte.config.js |
event.platform.env |
| 3 | @sveltejs/adapter-cloudflare@^8 |
vite.config.js |
import { env } from "cloudflare:workers" |
- Migrate hosting on your current SvelteKit major version. Do not combine the move with a SvelteKit major upgrade, unless your version is earlier than 2.
- On SvelteKit 2, do not use automatic project configuration or
npx sv add. Both install adapter version 8, which only works with SvelteKit 3. - Make sure
package.jsonhas anamefield. Without it,sv addfails and automatic configuration does not install the adapter. - Review the steps in Before you begin that apply to every framework.
Copy this prompt into your coding agent. The agent stops before it deploys.
Migrate this SvelteKit app from Vercel to Cloudflare Workers (Workers with Static Assets, not Pages).
0. Detect the package manager from the lockfile (<pm>) and install dependencies. If the lockfile comes from an older package manager major version, regenerate it in its own commit. With pnpm 10 or later, run `pnpm approve-builds esbuild workerd` if an install reports ERR_PNPM_IGNORED_BUILDS. If package.json has no "name", add one.
1. Detect the installed @sveltejs/kit version from node_modules/@sveltejs/kit/package.json.
- 2.x or 3.x: keep that major version. Do not combine the hosting move with a SvelteKit major upgrade.
- Earlier than 2 (1.x or 1.0.0-next.*): no Cloudflare Workers adapter supports it. Upgrade to the latest SvelteKit 2 in a separate commit, and keep the current adapter. Target @sveltejs/kit@^2, svelte@^5 (keep the legacy syntax), vite@^7, @sveltejs/vite-plugin-svelte@^6, typescript@^5, and svelte-check@^4. `npx sv migrate` is interactive and has no codemod for pre-1.0 APIs, so make these changes by hand:
- kit.methodOverride and POST, PATCH, or DELETE exports in +page.server become named `export const actions`, with `action="?/name"`.
- Custom form enhance functions become `enhance` from $app/forms.
- $app/env becomes $app/environment.
- `export const hydrate` and `export const router` become `csr`.
- sveltekit:prefetch becomes data-sveltekit-preload-data.
- src/hooks.js becomes src/hooks.server.js.
- `throw error()` and `throw redirect()` become `error()` and `redirect()`.
- Self-closing non-void tags, such as `<button />`, become `<button></button>`.
Get `build` and `check` passing before you continue.
2. Remove Vercel:
- Uninstall @sveltejs/adapter-vercel and @sveltejs/adapter-auto.
- Port redirects and headers from vercel.json. Static rules go to static/_redirects and static/_headers. Dynamic rules go to src/hooks.server.ts. Then delete vercel.json and .vercel/.
- Remove every Vercel route configuration (`export const config = { runtime, regions, split, memory, maxDuration, isr }`). List the ISR routes, and replace them with `export const prerender = true` or Cache-Control headers set with setHeaders.
- Remove VERCEL_* environment variable usage and Vercel analytics. Tell me to turn on Cloudflare Web Analytics instead.
3. Install @sveltejs/adapter-cloudflare and wrangler as dev dependencies. Use @^7 for SvelteKit 2 and @^8 for SvelteKit 3. Do not use `npx sv add` or `wrangler setup` on SvelteKit 2.
4. Configure the adapter. On SvelteKit 3, use sveltekit({ adapter: adapter() }) in vite.config. On SvelteKit 2, set kit.adapter = adapter() in svelte.config.js.
5. Create wrangler.jsonc with "name" (the package.json name), "main": ".svelte-kit/cloudflare/_worker.js", "compatibility_date": "<today>", "assets": { "binding": "ASSETS", "directory": ".svelte-kit/cloudflare" }, and "observability": { "enabled": true }. Do not add nodejs_compat. A compatibility date of 2026-08-04 or later already enables it. Add .wrangler/ and .dev.vars* to .gitignore.
6. Bindings: on SvelteKit 3, use `import { env, waitUntil } from "cloudflare:workers"`. On SvelteKit 2, use event.platform.env, and type App.Platform in src/app.d.ts as { env: Env; ctx: ExecutionContext; cf?: IncomingRequestCfProperties }. Run `npx wrangler types` before the first build. Add `/// <reference path="../worker-configuration.d.ts" />` to the top of src/app.d.ts instead of changing tsconfig "include".
7. Set these scripts: preview="<pm> run build && wrangler dev", deploy="<pm> run build && wrangler deploy", and cf-typegen="wrangler types".
8. Remove runtime file system access. Use read() from $app/server, or prerender. Do not use sharp at runtime.
9. If vercel.json had crons, tell me that adapter-cloudflare has no scheduled handler. Propose a separate Worker with a Cron Trigger that calls an authenticated SvelteKit endpoint.
10. Run `<pm> run build`, `<pm> run check`, and `npx wrangler dev --port <port>`. Request every page, __data.json endpoint, form action, and asset with curl. For form actions, send an Origin header that matches the dev URL. Use http://localhost, not 127.0.0.1, because SvelteKit sets Secure cookies on other hosts. If a third-party API also fails on the live Vercel site, report it instead of fixing it.
Rules: one commit per logical step; store local secrets in a gitignored .dev.vars file with placeholder values; never print secret values; do not deploy, log in, or change DNS.
When you finish, list the files you changed, behavior changes from any SvelteKit upgrade, the Vercel features without an equivalent, the secrets I must set with `npx wrangler secret put <NAME>`, and the deploy command.-
If your SvelteKit version is earlier than 2, upgrade to SvelteKit 2 first. For more information, refer to Migrating to SvelteKit v2 ↗︎.
-
Remove whichever of these adapters your project uses:
npm uninstall @sveltejs/adapter-vercel @sveltejs/adapter-autoyarn remove @sveltejs/adapter-vercel @sveltejs/adapter-autopnpm remove @sveltejs/adapter-vercel @sveltejs/adapter-autobun remove @sveltejs/adapter-vercel @sveltejs/adapter-autoInstall the Cloudflare adapter that matches your SvelteKit version. On SvelteKit 2:
npm i -D @sveltejs/adapter-cloudflare@^7 wrangleryarn add -D @sveltejs/adapter-cloudflare@^7 wranglerpnpm add -D @sveltejs/adapter-cloudflare@^7 wranglerbun add -d @sveltejs/adapter-cloudflare@^7 wranglerOn SvelteKit 3:
npm i -D @sveltejs/adapter-cloudflare@^8 wrangleryarn add -D @sveltejs/adapter-cloudflare@^8 wranglerpnpm add -D @sveltejs/adapter-cloudflare@^8 wranglerbun add -d @sveltejs/adapter-cloudflare@^8 wrangler -
Configure the adapter. On SvelteKit 2, edit
svelte.config.js:svelte.config.jsjs import adapter from "@sveltejs/adapter-cloudflare"; /** @type {import('@sveltejs/kit').Config} */ export default { kit: { adapter: adapter(), }, };On SvelteKit 3, pass the adapter to the
sveltekit()plugin invite.config.jsinstead. -
Create a Wrangler configuration file:
{ "$schema": "node_modules/wrangler/config-schema.json", "name": "<YOUR_PROJECT_NAME>", "main": ".svelte-kit/cloudflare/_worker.js", // Set this to today's date "compatibility_date": "2026-11-06", "assets": { "binding": "ASSETS", "directory": ".svelte-kit/cloudflare", }, "observability": { "enabled": true, }, }"$schema" = "node_modules/wrangler/config-schema.json" name = "<YOUR_PROJECT_NAME>" main = ".svelte-kit/cloudflare/_worker.js" # Set this to today's date compatibility_date = "2026-11-06" [assets] binding = "ASSETS" directory = ".svelte-kit/cloudflare" [observability] enabled = true -
Generate types for your bindings before the first build:
npx wrangler typesyarn wrangler typespnpm wrangler types -
On SvelteKit 2, reference the generated types and declare the platform type in
src/app.d.ts:src/app.d.tsts /// <reference path="../worker-configuration.d.ts" /> declare global { namespace App { interface Platform { env: Env; ctx: ExecutionContext; cf?: IncomingRequestCfProperties; } } } export {}; -
Delete
vercel.jsonand every Vercelexport const configblock. Replace ISR routes with prerendering orCache-Controlheaders. -
Build your application and run it locally:
npm run buildyarn run buildpnpm run buildnpx wrangler devyarn wrangler devpnpm wrangler dev -
Deploy your application:
npx wrangler deployyarn wrangler deploypnpm wrangler deploy
Upgrade checklist for SvelteKit versions earlier than 1.0
Pre-1.0 SvelteKit applications need these changes when you upgrade to SvelteKit 2:
| Pre-1.0 API | SvelteKit 2 replacement |
|---|---|
kit.methodOverride and POST/PATCH/DELETE exports |
Named form actions with action="?/name" |
Custom form enhance helpers |
enhance from $app/forms |
$app/env |
$app/environment |
export const hydrate and export const router |
export const csr |
sveltekit:prefetch |
data-sveltekit-preload-data |
src/hooks.js |
src/hooks.server.js |
throw error() and throw redirect() |
error() and redirect() |
Self-closing non-void tags, such as <button /> |
<button></button> |
After the upgrade, cookies set by SvelteKit include Secure and SameSite=Lax. Cross-site form submissions return 403.
- The build fails with
Cannot read properties of undefined (reading 'assets'). You installed adapter version 8 on SvelteKit 2. Install@sveltejs/adapter-cloudflare@^7. - The build fails with
Types file not found. Runnpx wrangler typesbefore you build. - Cron jobs have no scheduled handler.
adapter-cloudflaredoes not export ascheduled()handler. Use a separate Worker with a Cron Trigger that calls an authenticated endpoint. - Prerendered pages skip your hooks. Workers serves prerendered pages as static assets, so
hooks.server.jsdoes not run for them. Vercel behaves the same way.