Environment variables and modes

Vite · Essentials · 10 min

Every app needs per-environment configuration, and leaking a secret into the bundle is a classic mistake.

Vite exposes environment information on import.meta.env:

  • Built-ins: MODE (development, production or your custom mode), DEV, PROD, SSR, BASE_URL.
  • Your variables, only if prefixed with `VITE_`: import.meta.env.VITE_API_URL. Others (like DB_PASSWORD) are not exposed, by design.

Values come from .env files in the project root, loaded in this order with later ones winning:

  • .env (always), .env.local (always, git-ignored)
  • .env.[mode], .env.[mode].local

Variables already set in the shell have the highest priority: VITE_API_URL=... vite build.

The mode defaults to development for vite and production for vite build; choose another with --mode staging, which loads .env.staging. Mode is separate from NODE_ENV.

Everything exposed is inlined into the JavaScript at build time as a string. That means:

  • Never put secrets in VITE_ variables: anyone can read them in the bundle.
  • Values are strings; convert "true" or "3" yourself.
  • Changing a variable requires a rebuild, not just a redeploy. For runtime configuration, fetch a config.json at start-up instead.

Inside vite.config.ts, .env files are not loaded automatically; use loadEnv(mode, process.cwd()) if config needs them.

Example

# .env
VITE_API_URL=https://api.example.com
DB_PASSWORD=hunter2            # NOT exposed: no VITE_ prefix

# .env.staging
VITE_API_URL=https://staging-api.example.com

// src/api.js
console.log(import.meta.env.MODE);          // "development" | "production" | "staging"
console.log(import.meta.env.VITE_API_URL);  // inlined at build time
console.log(import.meta.env.DB_PASSWORD);   // undefined
const debug = import.meta.env.VITE_DEBUG === "true"; // values are strings

# build for staging
npx vite build --mode staging

Remember

  • Only VITE_-prefixed variables reach client code
  • .env, .env.local, .env.[mode], .env.[mode].local; shell wins
  • Values are inlined at build time as strings
  • Never put secrets in VITE_ variables

Watch out for

  • Storing an API secret in VITE_STRIPE_SECRET and shipping it to every browser.

Check yourself

  1. Why can't a `VITE_` variable be changed by restarting the deployed static site?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: vite.dev/guide/env-and-mode