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,productionor your custom mode),DEV,PROD,SSR,BASE_URL. - Your variables, only if prefixed with `VITE_`:
import.meta.env.VITE_API_URL. Others (likeDB_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.jsonat 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 stagingRemember
- 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.