SvelteKit on Cloudflare Workers

SvelteKit is the full-stack framework for Svelte. It uses adapter-cloudflare for Workers deployment. The dev server runs in Node.js (no CF Vite Plugin), so there’s a gap between dev and prod environments.

Create a New Project

npm create cloudflare@latest my-svelte-app -- --framework=svelte

Or add Cloudflare to an existing SvelteKit project:

npm install -D @sveltejs/adapter-cloudflare

Project Structure

my-svelte-app/
├── src/
│   ├── routes/
│   │   ├── +page.svelte      # Page component
│   │   ├── +page.server.ts   # Server-side data loading
│   │   └── +layout.svelte    # Layout
│   └── app.html               # HTML template
├── svelte.config.js           # SvelteKit + adapter config
└── wrangler.jsonc             # Cloudflare config

SvelteKit Config

// svelte.config.js
import adapter from "@sveltejs/adapter-cloudflare";
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";

export default {
  preprocess: vitePreprocess(),
  kit: {
    adapter: adapter(),
  },
};

Gotcha: adapter-cloudflare-workers is deprecated. Use adapter-cloudflare (without the -workers suffix). The deprecated package targeted the old Workers Sites, not Workers Static Assets.

Page with Server Data

<!-- src/routes/+page.svelte -->
<script>
  let { data } = $props();
</script>

<h1>Welcome</h1>
<p>Visits: {data.visits}</p>
// src/routes/+page.server.ts
import type { PageServerLoad } from "./$types";

export const load: PageServerLoad = async ({ platform }) => {
  const visits = await platform?.env?.MY_KV?.get("visits");
  return { visits: Number(visits || 0) };
};

API Route

// src/routes/api/hello/+server.ts
import type { RequestHandler } from "./$types";

export const GET: RequestHandler = async ({ platform }) => {
  const value = await platform?.env?.MY_KV?.get("greeting");
  return new Response(JSON.stringify({ message: value || "Hello" }), {
    headers: { "content-type": "application/json" },
  });
};

Binding Types

Generate types from your wrangler config:

npx wrangler types

Then reference them in src/app.d.ts:

// src/app.d.ts
declare global {
  namespace App {
    interface Platform {
      env: {
        MY_KV: KVNamespace;
        MY_DB: D1Database;
      };
    }
  }
}

export {};

Dev and Deploy

# Development (Node.js - no workerd parity)
npm run dev

# Build
npm run build

# Preview with wrangler (runs in workerd)
npx wrangler pages dev .svelte-kit/cloudflare

# Deploy
npx wrangler deploy

Tip: Always test with wrangler pages dev before deploying. The Node.js dev server won’t catch workerd runtime issues.

Key Points

  • platform.env gives you access to Cloudflare bindings
  • File-based routing in src/routes/
  • +page.server.ts runs on the server, +page.svelte renders the component
  • Dev runs in Node.js, not workerd (no CF Vite Plugin integration yet)
  • adapter-cloudflare targets Workers Static Assets
  • Svelte 5 runes ($state, $derived, $effect) work fine on Cloudflare

Gotcha: platform is undefined during npm run dev since there’s no workerd runtime. Use optional chaining (platform?.env?.MY_KV) or mock bindings in dev.