Menu
svelte-smol docs

SvelteKit 3

The same package supports SvelteKit 3 (currently a prerelease). The adapter detects which SvelteKit is building the app and runs the matching implementation, so there is nothing to switch. On SvelteKit 3, static assets and prerendered pages are embedded in the executable, and the options follow the official @sveltejs/adapter-bun.

Options from the other SvelteKit version fail the build with an error naming them, see Migrating from the SvelteKit 2 options.

#Install

bun add -d @orochibraru/svelte-smol @sveltejs/kit@next

#Usage

// vite.config.js
import { sveltekit } from "@sveltejs/kit/vite";
import { defineConfig } from "vite";
import adapter from "@orochibraru/svelte-smol";

export default defineConfig({
  plugins: [sveltekit({ adapter: adapter() })],
});

The build runs under the Bun runtime:

bun run --bun vite build

#Output

build/
├── server        # the executable, assets embedded
└── healthcheck   # tiny probe for Docker HEALTHCHECK
./build/server

Static assets and prerendered pages are served with an ETag, Last-Modified and Accept-Ranges: bytes, the same way whether they are embedded or on disk. A single byte Range gets a 206 (so <video> can seek without downloading the whole file), a range past the end a 416, and If-Range is honoured. Multiple ranges and malformed headers are ignored and answered with the full body. Ranges always apply to the uncompressed file, never to a .br / .gz variant.

#buildOptions.compile: false

A native (.node) addon like sharp can't be embedded in a binary. Set buildOptions: { compile: false } to emit a plain bundle instead:

build/
├── index.js       # the server entry, run with `bun`
├── server/        # split chunks
├── healthcheck    # still a compiled binary
├── client/
└── prerendered/
bun ./build/index.js

Pure-JS dependencies are still bundled. Native addons resolve from node_modules at runtime, so ship a production install alongside.

#Options

adapter({
  out: "build", // output directory
  precompress: false, // .br/.gz variants (compile: false only)
  envPrefix: "", // prefix for the runtime env vars below
  serverOptions: {}, // JSON-serializable Bun.serve() defaults; env vars win
  buildOptions: {
    compile: true, // true | "bun-linux-x64-musl" | { target, outfile, … } | false
    // also: sourcemap, minify, bytecode, banner, footer, drop, features,
    // optimizeImports, splitting
  },
  healthcheck: true, // or { path: "/healthz" }, or false
});

compile accepts any Bun compile target string ("bun-linux-x64", "bun-linux-arm64-musl", "bun-darwin-arm64", "bun-windows-x64", …) to cross-compile; the healthcheck binary uses the same target.

#Runtime environment variables

VariableDefaultPurpose
HOST0.0.0.0Listen address
PORT3000Listen port
SOCKET_PATHnoneListen on a Unix socket instead of HOST/PORT
ORIGINnonePublic origin, e.g. https://my.site; overrides paths.origin
REUSE_PORTfalseSO_REUSEPORT
IPV6_ONLYfalseDisable dual-stack
CONNECTION_IDLE_TIMEOUT10Bun socket idle timeout in seconds, max 255 (SSE responses opt out)
BODY_SIZE_LIMIT512KMax request body (K/M/G suffixes, or Infinity)
SHUTDOWN_TIMEOUT30Seconds to drain in-flight requests on SIGINT/SIGTERM
DEVELOPMENTfalseBun.serve development mode
PROTOCOL_HEADERnoneHeader carrying the forwarded protocol (e.g. x-forwarded-proto)
HOST_HEADERnoneHeader carrying the forwarded host
PORT_HEADERnoneHeader carrying the forwarded port
ADDRESS_HEADERnoneHeader carrying the client address (e.g. x-forwarded-for)
XFF_DEPTH1Trusted-proxy depth when ADDRESS_HEADER=x-forwarded-for
HEALTHCHECK_PATH/_healthEndpoint the healthcheck binary probes
HEALTHCHECK_TIMEOUT2000healthcheck binary request timeout in ms

Set envPrefix to namespace these (envPrefix: "MY_APP_" → MY_APP_PORT). The public origin comes from ORIGIN, read at runtime so one build can be deployed at several origins, then SvelteKit's paths.origin config, then the request headers above.

#Health check

With healthcheck enabled (the default) the server answers GET /_health with { "status": "ok", "uptime": … }, and the build also produces build/healthcheck, which probes it over loopback (or the Unix socket) and exits 0 when healthy, 1 otherwise:

HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
    CMD ["./build/healthcheck"]

It reads the same HOST / PORT / SOCKET_PATH as the server.

#Migrating from the SvelteKit 2 options

SvelteKit 2SvelteKit 3
compile: falsebuildOptions: { compile: false }
target: "bun-linux-x64"buildOptions: { compile: "bun-linux-x64" }
name: "app"buildOptions: { compile: { outfile: "app" } }
bytecode, minify, sourcemapbuildOptions.*
serveOptionsserverOptions (JSON-serializable only)
serveAssets: falseremoved, assets are embedded
IDLE_TIMEOUTCONNECTION_IDLE_TIMEOUT
ASSETS_DIRremoved, assets are embedded

This guide lives in the project repo: edit it there, and this page follows with the next release.