Environment Variables

Declare SvelteKit environment variables with Effect Schema validators.

SvelteKit 3 declares environment variables in src/env.ts and validates every value at startup. DefineEnvVars is a thin wrapper over SvelteKit's defineEnvVars that lets those declarations use Effect Schema directly, so configuration validation speaks the same language as the rest of an Effect application.

Declare variables

Export the declarations as variables from src/env.ts :

src/env.ts
import { DefineEnvVars } from "svelte-effect-runtime";
import { Schema } from "effect";

export const variables = DefineEnvVars({
	PORT: {
		schema: Schema.NumberFromString,
		description: "Port used by the server.",
	},
	DATABASE_URL: {
		schema: Schema.RedactedFromValue(Schema.String),
	},
	PUBLIC_ORIGIN: {
		public: true,
		static: true,
		schema: Schema.URLFromString,
	},
});

Every SvelteKit field passes through unchanged:

FieldDefaultPurpose
publicfalseExposes the variable to browser code through $app/env/public.
staticfalseInlines the build-time value so dead code can be eliminated.
descriptionEditor hover documentation for the generated export.
schemaAn Effect Schema or Standard Schema decoding the raw string.

Effect Schemas are converted with Schema.toStandardSchemaV1, existing Standard Schema validators pass through untouched, and a declaration without a schema keeps SvelteKit's non-empty-string default.

Consume decoded values

SvelteKit validates each value once at startup, so the generated exports are plain constants with the schema's decoded types. Import them directly — no wrapper, no yield*:

src/lib/server/database.ts
import { DATABASE_URL, PORT } from "$app/env/private";
import { Redacted } from "effect";

export const connect = () =>
	open_pool({
		port: PORT,
		url: Redacted.value(DATABASE_URL),
	});

PORT arrives as a number, PUBLIC_ORIGIN as a URL, and DATABASE_URL as a Redacted<string> that prints as <redacted> if it ever reaches a log line or serialized output. SvelteKit keeps ownership of loading, visibility, and the server-only import guard: importing $app/env/private from browser code fails the build.

On this page