Skip to main content
Basic Svelte
Introduction
Reactivity
Props
Logic
Events
Bindings
Classes and styles
Actions
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!

Environment variables in process.env are also available via $env/static/private.

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"

Open src/routes/+page.server.js. Import PASSPHRASE from $env/static/private and use it inside the form action:

src/routes/+page.server
import { redirect, fail } from '@sveltejs/kit';
import { PASSPHRASE } from '$env/static/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', {
				path: '/'
			});

			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 '$env/static/private';
	let { form } = $props();
</script>
<script lang="ts">
	import { PASSPHRASE } from '$env/static/private';
	let { form } = $props();
</script>

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

  • +page.server.js
  • +layout.server.js
  • +server.js
  • any modules ending with .server.js
  • any modules inside src/lib/server

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

Static vs dynamic

The static in $env/static/private indicates that these values are known at build time, and can be statically replaced. This enables useful optimisations:

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

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

In some cases you might need to refer to environment variables that are dynamic — in other words, not known until we run the app. We’ll cover this case in the next exercise.

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>