Managing environment-specific configurations like API endpoints, feature flags, or secret keys is a fundamental challenge in frontend development. Hardcoding these values directly into your codebase is unsustainable, insecure, and error-prone when deploying to different environments. Vite, a modern and fast build tool, provides a streamlined and explicit way to handle environment variables, offering clarity and security for different deployment targets.
This guide dives into Vite's specific mechanisms for managing environment variables, moving beyond a simple overview to practical application with code examples, focusing on how and why these approaches work. We'll explore import.meta.env, .env files, and the concept of 'modes'.
Core Concept: Vite's Approach to Environment Variables
Vite's approach to environment variables differs significantly from traditional Node.js-based setups. While Node.js applications often rely on process.env, Vite applications primarily use import.meta.env within client-side code. This distinction is crucial because Vite performs a static replacement during the build process for variables prefixed with VITE_, making them available in your bundled frontend code.
Variables without the VITE_ prefix are not exposed to the client bundle. They are only accessible within the Vite config file (vite.config.js) or during Node.js server-side operations (e.g., custom Vite plugins or SSR setups). This mechanism helps prevent accidental exposure of sensitive information.
Defining Environment Variables with .env Files
To manage variables, Vite leverages .env files, similar to the popular dotenv library. These files are loaded based on the mode your application is running in. The mode is a string that indicates the environment, typically 'development' (for vite or vite dev) or 'production' (for vite build).
Vite provides a hierarchy for .env files:
-
.env: General defaults. -
.env.local: Local overrides, ignored by Git. -
.env.[mode]: Mode-specific overrides (e.g.,.env.development,.env.production). -
.env.[mode].local: Local, mode-specific overrides, ignored by Git.
Let's set up a common scenario with three files:
.env # General defaults
.env.development # Overrides for development mode
.env.production # Overrides for production mode
Here's an example of their content:
./.env
VITE_APP_NAME="My Awesome App"
VITE_API_BASE_URL="https://api.example.com/v1"
SECRET_API_KEY="this-should-not-be-exposed" # Not prefixed, thus not exposed to client
./.env.development
VITE_API_BASE_URL="http://localhost:3001/api/v1"
./.env.production
VITE_API_BASE_URL="https://prod-api.example.com/v1"
Explanation: In these files, we define key-value pairs. Variables prefixed with VITE_ (e.g., VITE_APP_NAME, VITE_API_BASE_URL) will be exposed to your client-side code via import.meta.env. Variables without this prefix (like SECRET_API_KEY) are not exposed to the client, making them suitable for server-side configurations or within vite.config.js. Vite automatically loads .env files based on the current mode, with .env.[mode] taking precedence over the generic .env.
Accessing Variables in Your Application
Within your frontend JavaScript or TypeScript code, you access these environment variables through the special import.meta.env object. This object is populated by Vite during the build or development server startup.
// src/main.js or src/App.vue
// Accessing custom environment variables
console.log("App Name:", import.meta.env.VITE_APP_NAME);
console.log("API Base URL:", import.meta.env.VITE_API_BASE_URL);
// Vite also exposes built-in environment flags
if (import.meta.env.DEV) {
console.log("Running in development mode");
} else if (import.meta.env.PROD) {
console.log("Running in production mode");
}
// Accessing the current mode string
console.log("Current Mode:", import.meta.env.MODE);
// Attempting to access an unprefixed variable will result in 'undefined'
console.log("Secret API Key (should be undefined):", import.meta.env.SECRET_API_KEY);
const element = document.getElementById('app-info');
if (element) {
element.textContent = `App: ${import.meta.env.VITE_APP_NAME}, API: ${import.meta.env.VITE_API_BASE_URL}`;
}
Explanation: import.meta.env is a special object that Vite populates with your environment variables. VITE_APP_NAME and VITE_API_BASE_URL are successfully logged, reflecting the values from the appropriate .env file for the active mode. Vite also provides useful boolean flags like DEV and PROD to conditionally execute code based on the current environment, eliminating the need to manually parse NODE_ENV. Note that import.meta.env.SECRET_API_KEY correctly returns undefined, preventing accidental exposure of sensitive data to the client, reinforcing the importance of the VITE_ prefix.
Running with Different Modes and Customizing Build Behavior
Vite commands inherently use a mode. When you run vite (or vite dev), it defaults to development mode. When you run vite build, it defaults to production mode. You can explicitly specify a mode using the --mode CLI flag.
This is particularly useful for environments like staging or test, which require their own distinct configurations without being 'development' or 'production'.
Let's add a build:staging script to package.json and a corresponding .env.staging file:
package.json
{
"name": "vite-env-example",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"build:staging": "vite build --mode staging",
"dev:staging": "vite --mode staging"
}
}
Now, create the ./.env.staging file:
./.env.staging
VITE_API_BASE_URL="https://staging-api.example.com/v1"
VITE_APP_ENVIRONMENT="staging"
Explanation: The scripts section in package.json demonstrates how to use the --mode flag. npm run dev:staging will start the development server using staging mode, causing Vite to load variables from .env.staging (and then .env). Similarly, npm run build:staging will create a production build configured specifically for the staging environment. This explicit mode control allows for distinct configurations tailored to various deployment pipelines without altering your core application code.
Common Mistakes / Gotchas
- Forgetting
VITE_prefix: This is the most common mistake. Only variables prefixed withVITE_are exposed to your client-side code viaimport.meta.env. Variables likeAPI_KEY=123will not be accessible in your components. If you need to access non-prefixed variables, they are typically forvite.config.jsor server-side scripts. - Using
process.env: This is a common habit from Node.js development. In a Vite frontend application,process.envis not available at runtime in the browser. Always useimport.meta.envfor client-side variables. - Exposing Sensitive Information: Even with the
VITE_prefix, remember that any variable exposed viaimport.meta.envis eventually bundled into your client-side JavaScript. This means it's publicly visible if someone inspects your deployed code. Never put truly sensitive information (like private API keys, database credentials) directly into.envfiles that will be used for client-side builds. Instead, use a backend proxy or server-side logic to handle such secrets securely. - Understanding
modevs.NODE_ENV: Whileimport.meta.env.MODEreflects the currentmode(e.g., 'development', 'production', 'staging'),import.meta.env.DEVandimport.meta.env.PRODare specific boolean flags Vite provides.NODE_ENVis not directly used by Vite for mode resolution but might be present as a fallback from other tools. Always rely onimport.meta.env.MODE,import.meta.env.DEV,import.meta.env.PRODfor consistency within Vite.
Key Takeaways
- Vite uses
import.meta.envfor client-side environment variables, notprocess.env. - Variables must be prefixed with
VITE_to be exposed to the client bundle. -
.envfiles (e.g.,.env,.env.development,.env.production,.env.[mode]) allow for environment-specific configuration. - The
--modeCLI flag explicitly controls which.envfiles Vite loads for development or build processes. - Never expose truly sensitive secrets directly to the client bundle; use a backend or server-side rendering for true privacy.
Conclusion
Effectively managing environment variables is crucial for building robust and adaptable frontend applications. Vite's clear and explicit mechanism, leveraging import.meta.env and mode-specific .env files, simplifies this process significantly. By understanding these conventions and avoiding common pitfalls, you can ensure your application behaves predictably across development, staging, and production environments, enhancing both security and developer experience. Implement these practices today to streamline your configuration workflow and enhance your application's deployment reliability.
Top comments (0)