Vite uses modes and .env files for environment-based configuration: modes define the runtime context (development, production), while .env files provide environment-specific variables, with only VITE_ prefixed variables exposed to client code for security
Vite provides a powerful and flexible system for environment-based configuration through two complementary concepts: modes and environment variables. Modes define the context in which your application runs (e.g., development, production, staging), while environment variables (.env files) provide configuration values specific to each context. This separation allows you to build your application once and deploy it across multiple environments with different settings. Vite's approach emphasizes security by requiring explicit prefixes for client-exposed variables, preventing accidental leakage of sensitive server-side information [citation:2][citation:5].
import.meta.env.MODE: The mode the app is running in (development, production, or custom mode) [citation:2][citation:5]
import.meta.env.BASE_URL: The base URL the app is being served from, determined by the base config option [citation:2][citation:5]
import.meta.env.PROD: Boolean indicating whether the app is running in production [citation:2][citation:5]
import.meta.env.DEV: Boolean indicating whether the app is running in development (always opposite of PROD) [citation:2][citation:5]
import.meta.env.SSR: Boolean indicating whether the app is running on the server [citation:2][citation:5]
Modes are a core concept in Vite's environment system. By default, the dev server runs in development mode, and the build command runs in production mode. This means when you run vite build, Vite automatically loads variables from .env.production if it exists. However, you can override the default mode using the --mode flag. For example, vite build --mode staging will load .env.staging while still performing a production build (NODE_ENV remains 'production'). This separation between mode and NODE_ENV gives you fine-grained control over configuration without affecting build optimizations [citation:2][citation:6].
Vite uses dotenv to load environment variables from files in your project root. The framework supports multiple file types with specific purposes and loading priorities. These files are automatically loaded at Vite startup, and changes require restarting the dev server [citation:2][citation:5][citation:6].
.env: Loaded in all cases, contains base configuration [citation:2][citation:5]
.env.local: Loaded in all cases but ignored by git (for local overrides) [citation:2][citation:5]
.env.[mode]: Only loaded in the specified mode (e.g., .env.production, .env.staging) [citation:2][citation:5]
.env.[mode].local: Mode-specific local overrides, ignored by git [citation:2][citation:5]
Environment variables are loaded with a specific priority order. Variables from mode-specific files (e.g., .env.production) take precedence over generic files (.env). Local files (.env.local, .env.[mode].local) have even higher priority for developer-specific overrides. However, the highest priority of all goes to environment variables already set in the shell when Vite is executed—for example, running VITE_SOME_KEY=123 vite build will override any value in .env files [citation:2][citation:5][citation:6].
A critical security feature in Vite is that only environment variables prefixed with VITE_ are exposed to client-side code. Variables without this prefix are only available in server-side contexts like vite.config.js. This prevents accidental exposure of sensitive information like database passwords or API keys in the browser. When accessed in client code, these variables are statically replaced at build time—they don't actually exist as runtime environment variables [citation:2][citation:5][citation:6].
It's crucial to understand that NODE_ENV and mode are two different concepts in Vite. NODE_ENV (process.env.NODE_ENV) primarily controls build optimizations and framework behavior, while mode determines which .env file is loaded. This distinction allows you to have, for example, a staging build that runs with production optimizations (NODE_ENV=production) but uses staging-specific configuration loaded from .env.staging [citation:2][citation:5][citation:6].
For better developer experience, Vite allows you to add TypeScript type definitions for your custom environment variables. Create a vite-env.d.ts file in your src directory and augment the ImportMetaEnv interface. This provides autocompletion and type checking when using import.meta.env in your code [citation:2][citation:5][citation:6].
Vite also supports environment variable replacement in HTML files using a special %ENV_NAME% syntax. This is useful for setting meta tags, titles, or other HTML attributes based on environment. Variables that don't exist are left untouched, unlike JavaScript where they'd become undefined [citation:2][citation:5][citation:6].
Vite uses dotenv-expand to support variable expansion within .env files. This allows you to reference other variables, making configuration more DRY. However, if you need to use the $ character literally, you must escape it with a backslash [citation:2][citation:5].
When working with environment variables in Vite, follow these security practices: Never store sensitive information in VITE_* prefixed variables—they end up in client bundles. Use .env.local and .env.*.local files for local overrides and ensure they're in .gitignore. For production secrets, use actual server environment variables or a secrets management service. Remember that .env files are for development convenience, not secure secret storage [citation:2][citation:5][citation:6].