Environment variables: the $env modules
Environment variables are settings defined outside the code: a database address, a payment service key, the site name. Secrets must not be stored in the code or in git, and on a server the values often differ from your local ones. SvelteKit reads such variables through special $env modules.
The .env file
For development, variables are written to a .env file in the project root, next to package.json:
# .env
DATABASE_URL=postgres://localhost:5432/blog
API_KEY=secret-123
PUBLIC_SITE_NAME=My blogThe .env file is already listed in .gitignore of a project created with sv and does not get into the repository. After changing .env, restart npm run dev.
Secrets: $env/static/private
// src/lib/server/db.js
import { DATABASE_URL, API_KEY } from '$env/static/private';
export function connectionInfo() {
return `Connecting to ${DATABASE_URL}, key length ${API_KEY.length}`;
}The $env/static/private module can be imported only in server code: +page.server.js, +server.js, hooks.server.js, src/lib/server. If you import it in a component, the build stops with an error — the secret never goes to the browser.
Public values: $env/static/public
Variables whose names start with PUBLIC_ can be used in the browser too:
<!-- src/routes/+layout.svelte -->
<script>
import { PUBLIC_SITE_NAME } from '$env/static/public';
let { children } = $props();
</script>
<header>{PUBLIC_SITE_NAME}</header>
{@render children()}The header of every page shows "My blog". Public variables are visible to any visitor in the page code — do not put anything secret into them.
Static and dynamic
| Module | When it is read | Where allowed |
|---|---|---|
$env/static/private | at build time | server only |
$env/static/public | at build time | everywhere |
$env/dynamic/private | when the server starts | server only |
$env/dynamic/public | when the server starts | everywhere |
Static values are inlined into the code during npm run build. If the values are set on the server at startup — for example, the same build runs against different databases — use a dynamic module:
// src/hooks.server.js
import { env } from '$env/dynamic/private';
export async function handle({ event, resolve }) {
event.locals.maintenance = env.MAINTENANCE === 'on';
return resolve(event);
}In the dynamic modules the variables live in the env object and are read as its properties.
Common mistake
Reading variables through process.env.API_KEY. In development Vite does not load .env into process.env, so the value is undefined; in the browser there is no process at all. In a SvelteKit project always use the $env modules.