svelte-hono

Why this exists

You'd think rendering Svelte on a Cloudflare Worker would be easy. It is โ€” but only if you compile at build time.

The codegen wall

Cloudflare Workers run on a V8 isolate with code generation from strings disabled. eval, new Function, and friends all throw at runtime:

EvalError: Code generation from strings disallowed for this context

That's a security feature, not a bug. It rules out a popular pattern: compile Svelte source on the edge with svelte/compiler, evaluate the compiled output via new Function, render via svelte/server.

Several public projects appear to do this. They don't. Look at the request flow โ€” anything claiming "edge SSR via runtime compile" on Workers is either skipping SSR (client-only mount) or quietly returning a 500 in production.

The fix

Move the compile to build time. esbuild-svelte in server mode produces an ES module that imports svelte/internal/server normally. Workers can import ES modules. Workers can call functions. There's no string to evaluate.

At request time:

  1. Import the SSR module (built in advance).
  2. Call render(component, { props }) from svelte/server.
  3. Wrap the HTML in a shell with an inline script that hydrates from the pre-built client bundle.

What you lose

What you keep

Cache defaults

The whole reason a hydration script under a stable URL is dangerous is that browsers and Cloudflare's edge will happily serve the old bytes after you deploy. svelte-hono sidesteps that by content-hashing every client bundle: /__svelte/<id>.<hash>.js. New build โ†’ new hash โ†’ new URL โ†’ guaranteed cache miss.

URLCache-ControlWho serves it
GET / (HTML)public, max-age=0, must-revalidate (default)Worker, every request
GET /__svelte/{id}.{hash}.jspublic, max-age=31536000, immutableCloudflare edge cache, indefinitely
GET /__svelte/_runtime.{hash}.jspublic, max-age=31536000, immutableCloudflare edge cache, indefinitely

HTML revalidates on every request, so deploys are visible immediately, everywhere. Hashed asset URLs in that HTML mean the browser fetches fresh JS automatically โ€” no manual cache purging, ever. If a request comes in for a stale hash that no longer matches the current bundle, the Worker returns 404 instead of silently serving the wrong bytes.

Opt into edge HTML caching by passing cacheControl: "public, max-age=60, s-maxage=300" (or whatever) to svelteRenderer. Hashed asset URLs make even cached HTML safe โ€” it'll request the right JS as long as the HTML itself is still valid.

Why not SvelteKit?

SvelteKit is a full framework โ€” file-system router, layouts, load functions, an opinionated build, a Vite-based pipeline, an adapter per host. If you want all that, use it. @sveltejs/adapter-cloudflare is great.

This is for a different shape: "I'm already building a Hono Worker. I want components."

Why not just Hono JSX?

Hono JSX is excellent for SSR โ€” but no client-side reactivity. If your page is interactive, Svelte 5's signal-based runtime is genuinely small and pleasant. Use Hono JSX when the page is read-only.