Skip to main content
Basic Svelte
Introduction
Reactivity
Props
Logic
Events
Bindings
Classes and styles
Attachments
Transitions
Advanced Svelte
Advanced reactivity
Reusing content
Motion
Advanced bindings
Advanced transitions
Context API
Special elements
<script module>
Next steps
Basic SvelteKit
Introduction
Routing
Loading data
Headers and cookies
Shared modules
Forms
API routes
$app/state
Errors and redirects
Advanced SvelteKit
Hooks
Page options
Link options
Advanced routing
Advanced loading
Environment variables
Conclusion

Environment variables — like API keys and database credentials — can be added to a .env file, and they will be made available to your application.

You can also use .env.local or .env.[mode] files — see the Vite documentation for more information. Make sure you add any files containing sensitive information to your .gitignore file!

In this exercise, we want to allow the user to enter the website if they know the correct passphrase, using an environment variable.

First, in .env, add a new environment variable:

PASSPHRASE="open sesame"

Next, create src/env.js, import defineEnvVars from @sveltejs/kit/env, and declare PASSPHRASE:

src/env
import { defineEnvVars } from '@sveltejs/kit/env';

export const variables = defineEnvVars({
	PASSPHRASE: {}
});

Environment variables are private by default, so PASSPHRASE is now available from $app/env/private.

Open src/routes/+page.server.js. Import the named variable and use it inside the form action:

src/routes/+page.server
import { redirect, fail } from '@sveltejs/kit';
import { PASSPHRASE } from '$app/env/private';

export function load({ cookies }) {
	if (cookies.get('allowed')) {
		redirect(307, '/welcome');
	}
}

export const actions = {
	default: async ({ request, cookies }) => {
		const data = await request.formData();

		if (data.get('passphrase') === PASSPHRASE) {
			cookies.set('allowed', 'true');

			redirect(303, '/welcome');
		}

		return fail(403, {
			incorrect: true
		});
	}
};

The website is now accessible to anyone who knows the correct passphrase.

Keeping secrets

It’s important that sensitive data doesn’t accidentally end up being sent to the browser, where it could easily be stolen by hackers and scoundrels.

SvelteKit makes it easy to prevent this from happening. Notice what happens if we try to import PASSPHRASE into src/routes/+page.svelte:

src/routes/+page
<script>
	import { PASSPHRASE } from '$app/env/private';
	let { form } = $props();
</script>
<script lang="ts">
	import { PASSPHRASE } from '$app/env/private';
	let { form } = $props();
</script>

An error overlay pops up, telling us that $app/env/private cannot be imported into client-side code. It can only be imported into server-only modules, including:

  • SvelteKit server modules like +page.server.js, +layout.server.js, +server.js and hooks.server.js
  • modules with a server filename segment, like server.js, foo.server.js or foo.server.test.js
  • modules inside a server directory, except for server directories inside src/routes or static

In turn, these modules can only be imported by other server modules.

Dynamic vs static

Environment variables are dynamic by default — their values are read when the app runs, rather than being fixed when it is built. This means you can build the app once and deploy it to different environments with different values.

If a value is known at build time, you can add static: true to its definition. For example, add a feature flag to .env:

PASSPHRASE="open sesame"
FEATURE_FLAG_X=enabled

Then declare it as static:

src/env
import { defineEnvVars } from '@sveltejs/kit/env';

export const variables = defineEnvVars({
	PASSPHRASE: {},
	FEATURE_FLAG_X: {
		static: true
	}
});

Static values are inlined into your application code, enabling useful optimisations like dead-code elimination:

import { FEATURE_FLAG_X } from '$app/env/private';

if (FEATURE_FLAG_X === 'enabled') {
	// code in here will be removed from the build output
	// if FEATURE_FLAG_X is not enabled
}

Prior to SvelteKit 3 you didn’t have the option to declare an env.js file - instead, private environment variables were automatically available via $env/dynamic/private and $env/static/private.

Edit this page on GitHub

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
	let { form } = $props();
</script>
 
<form method="POST">
	<label>
		enter the passphrase
		<input name="passphrase" autocomplete="off" />
	</label>
</form>
 
{#if form?.incorrect}
	<p class="error">wrong passphrase!</p>
{/if}
 
<style>
	.error {
		color: red;
	}
</style>