onvibe.run

← All docs

SvelteKit on onvibe

Three approaches are available depending on whether you need SSR and how you want to manage the build output.

Important — onvibe has NO install or build phase. It does not run npm install or npm run build for you; it just serves the files you deploy. So every npm … command below runs in your own local environment (a machine with Node/npm). You build there, then upload only the build output with stage_files + deploy. Runtime npm dependencies still work via Deno's npm: specifier (e.g. import { Pool } from "npm:pg") — that needs no install step. If your assistant has no local shell to run the build (many MCP clients don't), prefer a plain Deno/TypeScript app or the web/api templates instead of a SvelteKit build.


Real server-side rendering on every request.

1. Install and configure

npm install -D @deno/svelte-adapter
// svelte.config.js
import adapter from "@deno/svelte-adapter";
export default { kit: { adapter: adapter({ out: "output" }) } };
npm run build
# Outputs to: output/server/ and output/client/

2. Write main.ts wrapper

The adapter's output cannot be used directly — write a main.ts that imports the server bundle and exports a default handler:

import { Server } from "./output/server/index.js";
import { manifest } from "./output/server/manifest.js";

const mimeTypes: Record<string, string> = {
  ".js": "application/javascript",
  ".css": "text/css",
  ".html": "text/html; charset=utf-8",
  ".png": "image/png",
  ".jpg": "image/jpeg",
  ".svg": "image/svg+xml",
  ".ico": "image/x-icon",
  ".woff2": "font/woff2",
  ".json": "application/json",
};

function mime(path: string): string {
  const ext = path.slice(path.lastIndexOf("."));
  return mimeTypes[ext] ?? "application/octet-stream";
}

const server = new Server(manifest);
await server.init({ env: Deno.env.toObject() });

export default async function handler(req: Request): Promise<Response> {
  const url = new URL(req.url);

  // Serve static assets from disk (output/client/)
  try {
    const file = await Deno.open(`./output/client${url.pathname}`);
    const stat = await file.stat();
    if (!stat.isDirectory) {
      const immutable = url.pathname.startsWith("/_app/immutable/");
      return new Response(file.readable, {
        headers: {
          "content-type": mime(url.pathname),
          "cache-control": immutable
            ? "public, max-age=31536000, immutable"
            : "no-cache",
        },
      });
    }
    file.close();
  } catch {
    // Not a static file — fall through to SSR
  }

  return server.respond(req, {
    getClientAddress: () => req.headers.get("x-forwarded-for") ?? "",
    platform: {},
  });
}

3. Deploy in chunks

The build output typically has 30–50 files. Use stage_files:

stage_files("my-app", [main.ts, output/server/index.js, ...chunk1])
stage_files("my-app", [output/client/_app/..., ...chunk2])
stage_files("my-app", [...remaining files])
deploy("my-app")

See onvibe://docs/large-deploy for the full chunked deploy workflow.


Option B — adapter-static (pre-rendered, no SSR)

Use when the app has no dynamic server-side logic. Simpler: the entire build fits in a single main.ts with pages and assets embedded as strings.

// svelte.config.js
import adapter from "@sveltejs/adapter-static";
export default { kit: { adapter: adapter() } };
// main.ts — embed build output as strings
const pages = new Map([
  ["/", "<!DOCTYPE html>..."],
  ["/about", "<!DOCTYPE html>..."],
]);
const assets = new Map([
  ["/_app/immutable/entry/start.js", "// bundled js"],
]);

export default async function handler(req: Request): Promise<Response> {
  const path = new URL(req.url).pathname;
  const asset = assets.get(path);
  if (asset) return new Response(asset, { headers: { "content-type": "application/javascript", "cache-control": "public, max-age=31536000, immutable" } });
  const page = pages.get(path) ?? pages.get("/");
  return new Response(page ?? "Not found", { status: page ? 200 : 404, headers: { "content-type": "text/html; charset=utf-8" } });
}

Option C — adapter-cloudflare (CF Workers export shape)

Use when you want to target the CF Workers runtime format. The platform runs the CF Workers module natively and provides an ASSETS shim for serving static files from disk.

1. Install and configure

npm install -D @sveltejs/adapter-cloudflare
// svelte.config.js
import adapter from "@sveltejs/adapter-cloudflare";
export default { kit: { adapter: adapter() } };
npm run build
# Outputs to: .svelte-kit/cloudflare/
# Main bundle: _worker.js
# Static assets: _app/, favicon.png, etc.

2. Write main.ts wrapper

Re-export the worker module as the default export:

// main.ts
export { default } from "./_worker.js";

The platform detects the CF Workers module shape (export default { fetch }) and provides:

3. Deploy in chunks

stage_files("my-app", [main.ts, _worker.js])
stage_files("my-app", [_app/immutable/..., favicon.png, ...])
deploy("my-app")

Notes


Which to choose

Option A (@deno/svelte-adapter) Option B (adapter-static) Option C (adapter-cloudflare)
SSR per request
API routes (+server.ts)
$state / reactivity ✅ (client only)
File count 30–50 files 1 file 10–30 files
Deployment stage_files + deploy deploy directly stage_files + deploy
Build target Deno-native static HTML CF Workers

Read this page as Markdown (best for LLMs) · plain text
onvibe.run · home · all docs