SvelteKit
Cloudflare.Website.SvelteKit deploys a
SvelteKit app as a Cloudflare Worker.
It builds the app with SvelteKit’s own Vite pipeline and a
wrangler-free in-memory Cloudflare adapter, then re-bundles the
server output for workerd. Client assets and prerendered pages deploy
as Worker static assets; dynamic routes are served by the generated
Worker. There is no svelte.config.js to write, no
@sveltejs/adapter-cloudflare to install, and no Wrangler file.
Install
Section titled “Install”The build integration is not bundled with alchemy — the resource
dynamically imports @distilled.cloud/sveltekit from your project at
deploy time, so it must be installed alongside your framework. It is
only used at build time, so a dev dependency is enough:
bun add -d @distilled.cloud/sveltekitnpm install -D @distilled.cloud/sveltekitpnpm add -D @distilled.cloud/sveltekityarn add -D @distilled.cloud/sveltekitConfigure SvelteKit
Section titled “Configure SvelteKit”There is no framework config to write. The resource drives
SvelteKit’s Vite pipeline programmatically and injects its own
Cloudflare adapter, so a fresh SvelteKit project deploys as-is.
Options that would normally live in svelte.config.js are passed via
the kit prop instead — see
Kit and adapter options below.
Declare the Website
Section titled “Declare the Website”Declare the site as a module-level const (rather than inline in the Stack) and derive the typed shape of its bindings from it:
import * as Cloudflare from "alchemy/Cloudflare";
export const Website = Cloudflare.Website.SvelteKit("Website");
export type WebsiteEnv = Cloudflare.InferEnv<typeof Website>;WebsiteEnv is the typed shape of the Worker’s bindings, derived
from the class — you’ll import it into your SvelteKit code in
Read bindings in server code.
Add it to the Stack
Section titled “Add it to the Stack”Yield the class from your Stack and return its URL:
import * as Alchemy from "alchemy";import * as Effect from "effect/Effect";
export default Alchemy.Stack( "MySvelteKitSite", { providers: Cloudflare.providers(), state: Cloudflare.state(), }, Effect.gen(function* () { const site = yield* Website; return { url: site.url }; }),);SvelteKit’s server code is built for Node, so the nodejs_compat
compatibility flag is added automatically — you don’t need to pass
it.
See examples/cloudflare-website-sveltekit for the checked-in example.
Add bindings
Section titled “Add bindings”The resource returns a plain Worker, so env accepts the full
binding vocabulary — KV namespaces, R2 buckets, Durable Objects,
secrets:
import * as Config from "effect/Config";
export const Cache = Cloudflare.KV.Namespace("Cache");
export const Website = Cloudflare.Website.SvelteKit("Website", { env: { CACHE: Cache, API_KEY: Config.redacted("API_KEY"), },});Cache is a description, not a deploy — Alchemy provisions the real
namespace because the Website binds it. Config.redacted reads
API_KEY from your environment at deploy time and binds it as a
Worker secret — see Secrets & env.
Read bindings in server code
Section titled “Read bindings in server code”Server routes read bindings through SvelteKit’s platform.env. Type
it once by augmenting App.Platform with the inferred env type:
import type { WebsiteEnv } from "../alchemy.run.ts";
declare global { namespace App { interface Platform { env: WebsiteEnv; } }}
export {};Every server route now gets fully typed bindings:
export const load = async ({ platform }) => { const cached = await platform?.env?.CACHE.get("greeting"); return { greeting: cached ?? "hello" };};platform.env.CACHE is typed as a KV namespace and
platform.env.API_KEY as a string — renaming a binding in
alchemy.run.ts is a type error in your routes.
Kit and adapter options
Section titled “Kit and adapter options”Since kit v3 the configuration lives in memory — pass it via the
kit prop instead of a svelte.config.js. The generated Cloudflare
adapter is configured via adapter:
export const Website = Cloudflare.Website.SvelteKit("Website", { kit: { alias: { $lib: "src/lib" }, }, adapter: { notFoundHandling: "404-page", fallback: "spa", },});Don’t set kit.adapter — Alchemy injects its own wrangler-free
Cloudflare adapter.
Local dev
Section titled “Local dev”bun alchemy devalchemy dev runs SvelteKit’s own Vite dev server — Node SSR with
full HMR. platform.env carries the Worker’s real Cloudflare
bindings, served wrangler-free through the cloudflare-runtime
platform proxy: calls like platform.env.CACHE.get(...) round-trip
to a local workerd instance, so dev state is live and shared. Literal
env values (strings and secrets) overlay the proxied bindings. Your
server code is identical in dev and production.