SvelteKit
SvelteKit is built on Vite, so there’s no dedicated SvelteKit package — which integration you use depends on where you deploy.
- Node / Vercel / Netlify / self-hosted / most adapters — use the Vite integration. See setup below.
- Cloudflare Workers (via
@sveltejs/adapter-cloudflare) — usevarlockSvelteKitCloudflarePluginfrom@varlock/cloudflare-integration/sveltekit. See setup below.
Check out the SvelteKit example project for a working reference.
Setup (Vite integration)
Section titled “Setup (Vite integration)”For any SvelteKit deployment target other than Cloudflare Workers, use varlockVitePlugin exactly as you would in any Vite project.
-
Install packages
Terminal window npm install @varlock/vite-integration varlockTerminal window pnpm add @varlock/vite-integration varlockTerminal window bun add @varlock/vite-integration varlockTerminal window yarn add @varlock/vite-integration varlockTerminal window vlt install @varlock/vite-integration varlock -
Run
varlock initto set up your.env.schemaTerminal window npm exec -- varlock initTerminal window pnpm exec -- varlock initTerminal window bun exec varlock initTerminal window vlx -- varlock initTerminal window yarn exec -- varlock init -
Add the plugin to your Vite config — it should come before
sveltekit():vite.config.ts import { sveltekit } from '@sveltejs/kit/vite';import { defineConfig } from 'vite';import { varlockVitePlugin } from '@varlock/vite-integration';export default defineConfig({plugins: [varlockVitePlugin(),sveltekit(),],});
Setup for Cloudflare Workers
Section titled “Setup for Cloudflare Workers”For SvelteKit projects deploying to Cloudflare Workers via @sveltejs/adapter-cloudflare, use varlockSvelteKitCloudflarePlugin from @varlock/cloudflare-integration/sveltekit. This is a separate entry point from the standard varlockCloudflareVitePlugin because @cloudflare/vite-plugin doesn’t currently support SvelteKit (see cloudflare/workers-sdk#8922). The SvelteKit plugin skips that dependency entirely and injects the runtime env-loader into SvelteKit’s SSR entry instead, so the resolved env is available inside the worker.
-
Install packages
Terminal window npm install @varlock/cloudflare-integration varlockTerminal window pnpm add @varlock/cloudflare-integration varlockTerminal window bun add @varlock/cloudflare-integration varlockTerminal window yarn add @varlock/cloudflare-integration varlockTerminal window vlt install @varlock/cloudflare-integration varlock -
Run
varlock initto set up your.env.schemaTerminal window npm exec -- varlock initTerminal window pnpm exec -- varlock initTerminal window bun exec varlock initTerminal window vlx -- varlock initTerminal window yarn exec -- varlock init -
Add the plugin to your Vite config
vite.config.ts import { sveltekit } from '@sveltejs/kit/vite';import { defineConfig } from 'vite';import { varlockSvelteKitCloudflarePlugin } from '@varlock/cloudflare-integration/sveltekit';export default defineConfig({plugins: [varlockSvelteKitCloudflarePlugin(),sveltekit(),],}); -
Deploy with
varlock-wranglerUse
varlock-wrangler deployinstead ofwrangler deployin your deploy script:package.json {"scripts": {"dev": "vite dev","build": "vite build","deploy": "npm run build && varlock-wrangler deploy"}}If you deploy via Cloudflare Workers Builds instead of a local/CI script, override the deploy command in your Cloudflare dashboard under Settings → Build → Deploy command — see Workers Builds below.
How it works
Section titled “How it works”- In dev:
vite devruns SvelteKit’s dev server; varlock resolves env via its normal flow and makes it available onENV.*. - In production: The SvelteKit SSR bundle has a
cloudflare:workersruntime loader injected at the top of its server entry. At worker boot, it reads the__VARLOCK_ENVbinding and hydrates varlock’s runtime. The loader is guarded by anavigator.userAgent === 'Cloudflare-Workers'check so SvelteKit’s Node-side postbuild steps (prerender, fallback) don’t try to resolvecloudflare:workers. varlock-wrangler deployuploads non-sensitive values as Cloudflare vars and sensitive values as secrets.
Deploying via Cloudflare Workers Builds
Section titled “Deploying via Cloudflare Workers Builds”If you deploy through Cloudflare Workers Builds, two pieces of configuration must be set in the dashboard — neither can be committed to the repo:
- Override the Deploy command — Under Settings → Build → Deploy command, replace the default
npx wrangler deploywithnpx varlock-wrangler deploy. Without this, Cloudflare runs stockwrangler deploy, which skips varlock resolution and leaves your worker without its resolved vars/secrets. - Set any secret-zero vars under Build variables — Any env vars varlock itself needs during load (e.g. a 1Password service account token, a GCP key) must be set under Settings → Build → Variables and Secrets so they’re available at build time.
varlock-wrangler deploythen resolves your full env graph and uploads the result to the worker runtime as regular vars/secrets.
Migrating from SvelteKit’s $env/*
Section titled “Migrating from SvelteKit’s $env/*”SvelteKit ships four built-in env modules — $env/static/private, $env/static/public, $env/dynamic/private, $env/dynamic/public — which split env vars along two axes: static vs dynamic (inlined at build vs looked up at runtime) and private vs public (server-only vs bundled for the browser, gated by the PUBLIC_ prefix).
The key conceptual shift is that SvelteKit’s system is access-driven: the same underlying variable behaves differently depending on which module you import it from, and public/private is inferred from the variable’s name. Varlock is schema-driven: each item is declared once in .env.schema with decorators that determine its behavior — sensitivity, type, validation, whether it’s inlined or resolved at runtime — and every access site uses the same ENV object. You describe the variable once, and that description is authoritative everywhere it’s used.
Varlock replaces all four $env/* modules with a single ENV object from varlock/env. The same two axes still exist, but they’re controlled by the schema rather than the import path:
| SvelteKit concept | Varlock equivalent |
|---|---|
$env/static/public | ENV.FOO where @sensitive=false — non-sensitive values are inlined at build time on both client and server |
$env/static/private | ENV.FOO where @sensitive=true, referenced from SSR/server code — inlined into the server bundle, build errors if referenced from client code |
$env/dynamic/public | Not yet supported — non-sensitive values are currently always inlined at build time (equivalent to $env/static/public). Runtime-resolved public values are on the roadmap. |
$env/dynamic/private | ENV.FOO — in server contexts it’s read from the live runtime env (e.g. Cloudflare bindings via varlockCloudflareVitePlugin, or Node’s process.env elsewhere) rather than baked into the bundle |
PUBLIC_ prefix requirement | Per-item @sensitive decorator, or a schema-wide @defaultSensitive rule |
Migrating imports
Section titled “Migrating imports”Everywhere you currently import from $env/*, switch to varlock/env:
import { API_KEY } from '$env/static/private';import { PUBLIC_API_URL } from '$env/static/public';import { ENV } from 'varlock/env';
export const load = async () => { const res = await fetch(`${PUBLIC_API_URL}/data`, { headers: { Authorization: `Bearer ${API_KEY}` }, const res = await fetch(`${ENV.PUBLIC_API_URL}/data`, { headers: { Authorization: `Bearer ${ENV.API_KEY}` }, });};ENV works from both +page.svelte (client) and +page.server.ts/+server.ts (server) — there’s no need to import from different paths depending on context. Varlock enforces the public/private boundary with both build-time checks (sensitive values are never included in the client bundle, and referencing one from client-reachable code surfaces as a build error) and runtime checks (log redaction and leak detection in server responses) so a mistake in either layer is caught rather than silently exposing a secret.
Managing sensitive values
Section titled “Managing sensitive values”SvelteKit uses the PUBLIC_ prefix to determine which vars are safe to bundle for the browser. Varlock uses decorators — either per-item, or schema-wide via @defaultSensitive.
The simplest setup is to mark items explicitly:
# @defaultSensitive=true# ---API_KEY= # sensitive by default# @sensitive=falsePUBLIC_API_URL= # explicitly non-sensitive (bundled for browser)If you’d rather keep SvelteKit’s PUBLIC_-prefix convention, you can infer sensitivity from the name:
# @defaultSensitive=inferFromPrefix('PUBLIC_')# ---API_KEY= # sensitive (no prefix)PUBLIC_API_URL= # non-sensitive (has PUBLIC_ prefix)