or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

examples

edge-cases.mdreal-world-scenarios.md
index.md
tile.json

environment-variables.mddocs/reference/

Environment Variables

Access environment variables with static imports (replaced at build time) or dynamic imports (accessed at runtime).

Core Imports

// Static public environment variables (build-time replacement)
import { PUBLIC_API_URL, PUBLIC_SITE_NAME } from '$env/static/public';

// Static private environment variables (server-only, build-time replacement)
import { DATABASE_URL, SECRET_KEY } from '$env/static/private';

// Dynamic public environment variables (runtime access)
import { env } from '$env/dynamic/public';

// Dynamic private environment variables (server-only, runtime access)
import { env } from '$env/dynamic/private';

Capabilities

Static Public Environment Variables

Publicly available environment variables that are replaced at build time. All variables must start with PUBLIC_ prefix.

// From '$env/static/public'
export const PUBLIC_*: string;

Usage:

import { PUBLIC_API_URL, PUBLIC_SITE_NAME } from '$env/static/public';

// Safe to use in client-side code
const response = await fetch(`${PUBLIC_API_URL}/posts`);
console.log('Site:', PUBLIC_SITE_NAME);

Example .env file:

PUBLIC_API_URL=https://api.example.com
PUBLIC_SITE_NAME=My Awesome Site
PUBLIC_ANALYTICS_ID=UA-12345678-9

Static Private Environment Variables

Private environment variables available only on the server that are replaced at build time. Can have any name (no PUBLIC_ prefix).

// From '$env/static/private'
export const *: string;

Usage:

// In +page.server.ts, +server.ts, hooks.server.ts, etc.
import { DATABASE_URL, SECRET_KEY, API_TOKEN } from '$env/static/private';

export async function load() {
  const db = await connectDatabase(DATABASE_URL);
  const data = await fetchData(API_TOKEN);
  return { data };
}

Example .env file:

DATABASE_URL=postgresql://localhost:5432/mydb
SECRET_KEY=super-secret-key-123
API_TOKEN=secret-api-token

Dynamic Public Environment Variables

Publicly available environment variables accessed at runtime through an env object.

// From '$env/dynamic/public'
export const env: Record<string, string>;

Usage:

import { env } from '$env/dynamic/public';

// Access variables dynamically
const apiUrl = env.PUBLIC_API_URL;
const featureFlag = env.PUBLIC_FEATURE_X === 'true';

// Iterate over all public env vars
Object.entries(env).forEach(([key, value]) => {
  console.log(`${key}: ${value}`);
});

Use cases:

  • When you need to iterate over environment variables
  • When variable names are determined at runtime
  • When you want smaller bundle sizes (variables not inlined)

Dynamic Private Environment Variables

Private environment variables accessed at runtime through an env object. Server-only.

// From '$env/dynamic/private'
export const env: Record<string, string>;

Usage:

// In server-only contexts
import { env } from '$env/dynamic/private';

export async function load() {
  // Access variables dynamically
  const dbUrl = env.DATABASE_URL;
  const apiKey = env.API_KEY;

  // Useful for plugin systems or dynamic configuration
  const plugins = Object.keys(env)
    .filter(key => key.startsWith('PLUGIN_'))
    .map(key => ({ name: key, config: env[key] }));

  return { plugins };
}

Notes

Static vs Dynamic

Static imports:

  • Variables are replaced at build time with their values
  • Results in smaller runtime code
  • Values are inlined in the bundle
  • Type-safe: each variable is a named export
  • Cannot iterate over all variables
  • More secure: unused variables are not included in bundle

Dynamic imports:

  • Variables are accessed at runtime from env object
  • All variables included in the bundle
  • Can iterate over variables
  • More flexible for dynamic access patterns
  • Slightly larger bundle size

Public vs Private

Public variables (PUBLIC_* prefix):

  • Available in both client and server code
  • Included in client-side JavaScript bundle
  • Safe for non-sensitive data only
  • Visible to anyone who inspects the client bundle
  • Examples: API URLs, feature flags, analytics IDs

Private variables (no prefix requirement):

  • Only available in server-side code
  • Never sent to the client
  • Safe for sensitive data
  • Available in: *.server.ts, hooks.server.ts, server-side load functions
  • Examples: database URLs, API keys, secrets

Configuration

Environment variables are loaded from:

  1. .env file (committed to repo, default values)
  2. .env.local file (not committed, local overrides)
  3. Process environment variables

Variables are loaded in this priority order:

  • process.env (highest priority)
  • .env.local
  • .env (lowest priority)

Security

  • Never put sensitive data in PUBLIC_* variables
  • Private variables are stripped from client bundle at build time
  • Use private variables for: API keys, database credentials, secrets
  • Use public variables for: API endpoints, feature flags, non-sensitive config

Type Safety

For better type safety, create type definitions:

// src/app.d.ts
declare namespace App {
  interface Platform {
    env: {
      PUBLIC_API_URL: string;
      PUBLIC_SITE_NAME: string;
      DATABASE_URL: string;
      SECRET_KEY: string;
    };
  }
}