@Vite

Environment Variables and Modes in Vite

Learn how Vite loads, exposes, types, and uses environment variables and modes in browser, SSR, and configuration code.

Vite environment variables let a project receive values from the shell or dotenv files without hard-coding every deployment setting into source code. Common examples include a public API base URL, a feature flag, or an application title.

This topic assumes basic JavaScript modules, command-line usage, development versus production builds, and the security difference between browser code and trusted server-side code.

Build-time values versus runtime configuration

Vite normally handles environment variables at build time. During development, Vite reads the environment and replaces references such as import.meta.env.VITE_API_BASE_URL. During a production build, it statically replaces those references in the generated client assets.

This differs from runtime configuration. A runtime value is read after the application has been built, such as from a server-rendered response, an API endpoint, or a configuration file served by a backend. A static Vite client bundle cannot safely receive a new private value merely because the hosting server has a different process environment; the value must be supplied during the build unless the application implements a separate runtime configuration mechanism.

Because public values are substituted into client code, users can inspect them in browser developer tools or downloaded assets. A prefix prevents accidental exposure, but a prefix is not encryption or access control.

The import.meta.env object

import.meta.env is a Vite-provided object containing built-in metadata and approved client environment variables. Use it instead of process.env in browser application code.

PropertyValue typeMeaningTypical use
MODEstringThe active Vite mode, such as development, production, or a custom mode.Select mode-specific behavior or assets.
BASE_URLstringThe public base path configured for the application.Build URLs that work when the app is served below a path.
PRODbooleanTrue when Vite is running a production build.Disable diagnostics or development helpers.
DEVbooleanTrue when Vite is running in development.Enable verbose logs or local debugging tools.
SSRbooleanIndicates server-side rendering execution.Separate browser-only and server-only code paths.

For example:

if (import.meta.env.DEV) {
console.debug('Extra diagnostics enabled');
}

if (import.meta.env.PROD) {
startProductionMonitoring();
}

Vite knows the values of built-in flags during transformation. When a condition uses a statically analyzable flag, the production bundler can perform dead-code elimination: it removes branches that can never run in the resulting build.

Environment files

A dotenv file contains declarations in KEY=value form. Vite recognizes shared, local, mode-specific, and mode-specific local files:

  • .env applies in every mode.
  • .env.local applies in every mode and is intended for machine-specific values.
  • .env.[mode] applies only to the selected mode, such as .env.staging.
  • .env.[mode].local applies only to the selected mode and is intended for local overrides.

A typical staging setup may contain:

# .env
VITE_API_BASE_URL=https://api.example.test

# .env.staging
VITE_API_BASE_URL=https://staging-api.example.test

# .env.staging.local
VITE_DEBUG_PANEL=true

When the same variable appears in several files, the more specific source wins. An already-existing shell or process environment variable has higher precedence than values loaded from files.

File or sourceApplies toTypical purposePrecedence
Existing shell or process environmentAny command receiving that variableCI, hosting platform, or deployment-specific overridesHighest
.env.[mode].localOne mode on one machinePrivate local override for that modeHigher than other dotenv files
.env.[mode]One modeShared staging or testing settingsBelow mode-local values
.env.localAll modes on one machineDeveloper-specific shared defaultsBelow mode-specific values
.envAll modesCommitted project defaultsLowest dotenv precedence

Local files commonly contain credentials, personal ports, or machine-specific settings, so add them to version control ignores. Commit a safe example file such as .env.example with placeholder values and documented variable names.

Modes

A mode is a named build or development context. The selected mode determines which mode-specific dotenv files Vite loads.

  • vite starts the development server with the default development mode.
  • vite build creates a production-oriented build with the default production mode.
  • vite --mode staging starts development using the staging mode.
  • vite build --mode staging creates a build using the staging mode.

For example, vite build --mode staging loads shared files and the staging files, including .env.staging and .env.staging.local when present. The active name is available as import.meta.env.MODE.

Vite mode is not necessarily the same as NODE_ENV. Mode selects Vite's named environment-file context. NODE_ENV is a conventional process setting used by many tools to describe development or production behavior.

ConceptWhat it controlsHow it is selectedCommon values
Vite modeVite's named context and mode-specific dotenv filesDefault command behavior or --modedevelopment, production, staging
NODE_ENVA conventional environment setting consumed by Node-based tools and application logicShell, scripts, hosting platform, or tool configurationdevelopment, production, test

Which variables reach browser code?

By default, only environment variables whose names begin with VITE_ are exposed through import.meta.env. For example:

# .env
VITE_API_BASE_URL=https://api.example.test
PAYMENT_PROVIDER_SECRET=private-value
const apiBaseUrl = import.meta.env.VITE_API_BASE_URL;

VITE_API_BASE_URL is available to browser code because it uses the default VITE_ prefix. PAYMENT_PROVIDER_SECRET is not available through import.meta.env because it has no allowed public prefix.

The envPrefix configuration option can allow additional prefixes:

import { defineConfig } from 'vite';

export default defineConfig({
envPrefix: ['VITE_', 'PUBLIC_']
});

After this configuration, names beginning with PUBLIC_ are also client-exposed. Never set the public prefix to an empty string: that can expose essentially all environment variables, including secrets.

Variable examplePrefix statusAvailable in browserSecurity guidance
VITE_PUBLIC_API_URLAllowed by defaultYesSafe only if the URL itself is intended to be public.
DATABASE_URLNo public prefixNoKeep it in trusted server-side code.
VITE_ANALYTICS_KEYAllowed by defaultYesUse only when the provider identifies it as a public browser key.
PAYMENT_SECRETNo public prefixNoNever copy it into client code or a client-exposed define value.

Using variables in application code

Access public values with statically written property names:

const apiBaseUrl = import.meta.env.VITE_API_BASE_URL;
const featureFlag = import.meta.env.VITE_FEATURE_FLAG;

Values read from dotenv files are strings. Therefore, the text false is a nonempty string and is truthy in JavaScript:

const enabled = import.meta.env.VITE_FEATURE_FLAG === 'true';
const retryLimit = Number.parseInt(import.meta.env.VITE_RETRY_LIMIT ?? '3', 10);

Property access should be statically analyzable. A direct expression such as import.meta.env.VITE_API_BASE_URL is reliable. Dynamically constructing a property name with bracket access, such as import.meta.env[key], may not be replaced or exposed as expected because Vite cannot determine the requested variable during transformation. If dynamic selection is needed, explicitly build a map from statically named properties.

Vite reads environment files when it starts. Restart the development server after changing a dotenv file. A browser refresh alone does not reload the changed values.

Variable expansion in dotenv files

Environment files can reference other variables. This is useful when one value is derived from another:

VITE_HOST=api.example.test
VITE_API_URL=https://${VITE_HOST}/v1

Expansion depends on the dotenv expansion implementation and its compatibility rules. Define referenced variables before the variables that use them, and avoid reverse-order references, circular references, and ambiguous naming. Explicit ordering makes the files easier to understand and more portable across tools.

Type support with TypeScript

Vite supplies default TypeScript declarations for ImportMetaEnv, including built-in properties and the general shape of import.meta.env. Project-specific public variables can be added with interface augmentation.

For example, place this in a declaration file included by the project, such as a Vite client environment declaration file:

interface ImportMetaEnv {
readonly VITE_API_BASE_URL: string;
readonly VITE_FEATURE_FLAG: string;
}

interface ImportMeta {
readonly env: ImportMetaEnv;
}

The declaration file should not contain imports when using this ambient interface-augmentation pattern. Also verify that the file is included by the relevant TypeScript configuration.

Typing a variable as string documents its raw form; it does not parse the value or guarantee that the variable exists at runtime. For stricter projects, declare each permitted public name explicitly and use a TypeScript configuration that checks the declaration file. This provides autocomplete and catches misspelled names, while runtime validation can still be added for required values.

Environment values in HTML

Vite can replace public environment placeholders in HTML entry files. HTML uses a placeholder format rather than JavaScript's import.meta.env syntax:

<title>%VITE_APP_TITLE%</title>

For a value such as VITE_APP_TITLE=Customer Portal, Vite replaces the placeholder while processing the HTML. In JavaScript, the equivalent access is import.meta.env.VITE_APP_TITLE.

If an HTML environment variable is missing, Vite replaces the placeholder with an empty string. Check required values during development or in the build pipeline if an empty result would produce an invalid page.

Loading environment values in Vite configuration

Environment files are not automatically available through import.meta.env while Vite is evaluating vite.config. Configuration code runs early because Vite needs the configuration to determine how to process the application.

Use Vite's loadEnv utility when configuration behavior depends on a mode-specific value:

import { defineConfig, loadEnv } from 'vite';

export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');

return {
define: {
__APP_ENV__: JSON.stringify(env.APP_ENV)
}
};
});

The first argument selects the active mode, the second identifies the project root, and the third is the prefix filter. Passing '' loads all matching environment values, including values without VITE_. That is useful for configuration logic but requires care: loading a value into the configuration does not make it safe to send to the browser.

To load only public-style values, pass a prefix such as 'VITE_'. Reading process.env directly reads values already supplied by the shell or process. It does not replace explicitly loading the dotenv files needed by configuration.

Security and deployment practices

  • Keep API keys, private access tokens, database credentials, payment provider secrets, and signing secrets on trusted servers.
  • Use server-side code, a platform secret store, or a backend proxy for sensitive operations.
  • Expose only values that a browser genuinely needs, such as a public API origin or a provider-designated browser key.
  • Commit a safe example file with placeholder values, and ignore developer-specific local files.
  • Review generated client assets when changing prefixes or configuration mappings.
  • Provide appropriate build-time values for each deployment environment. A staging build and a production build should not accidentally share endpoints or feature settings.

Practical patterns

Public API base URL

# .env
VITE_API_BASE_URL=https://api.example.test
const response = await fetch(
`${import.meta.env.VITE_API_BASE_URL}/users`
);

The URL is public because it is bundled into browser code. Authentication credentials, if required, must still be handled safely.

Development-only diagnostics

if (import.meta.env.DEV) {
enableVerboseLogging();
}

The static development condition allows production builds to remove the diagnostics branch through dead-code elimination.

Staging mode

vite build --mode staging

Vite uses .env, .env.staging, and available local variants. Mode-specific values override shared values, subject to process-environment precedence.

Keeping a secret private

# Used only by trusted server-side code
PAYMENT_PROVIDER_SECRET=private-value

Do not read this value from browser code. Implement the payment operation on a server or through a protected backend endpoint.

Troubleshooting

A variable is undefined in browser code

  • Confirm that the name begins with an allowed prefix, normally VITE_.
  • Confirm the active mode and the exact filename, such as .env.staging.
  • Restart Vite after editing an environment file.
  • Use import.meta.env, not process.env, in browser code.

A dotenv value does not override a terminal value

The shell or process value has higher precedence. Update or unset that value, or account for it in the deployment script.

A secret appears in the client bundle

The secret may have received an allowed public prefix or been copied into a client-exposed define value. Remove the exposure, rotate the credential, and move the operation to trusted server-side infrastructure.

Configuration cannot read a dotenv value

Call loadEnv(mode, process.cwd(), prefix) inside the configuration factory. Do not expect ordinary application injection to have happened before the configuration is evaluated.

A boolean flag is true when its file says false

Dotenv values are strings. Compare explicitly with 'true' or parse the value before using it:

const isEnabled = import.meta.env.VITE_FEATURE_FLAG === 'true';

TypeScript rejects a custom environment property

Add the property to an ImportMetaEnv augmentation, ensure the declaration file contains no imports for this ambient pattern, and verify that the file is included by tsconfig.

Exam-relevant summary

  • import.meta.env contains Vite metadata and approved client variables.
  • MODE selects the named Vite context; it is distinct from NODE_ENV.
  • Development defaults to development mode, while vite build defaults to production mode.
  • Only the default VITE_ prefix, or prefixes configured with envPrefix, are exposed to browser code.
  • All client-exposed values are public, regardless of their name or intended use.
  • Dotenv values are strings and should be parsed when used as booleans or numbers.
  • Restart Vite after changing environment files.
  • Use loadEnv when Vite configuration needs dotenv values.
  • Use TypeScript ImportMetaEnv augmentation to describe project-specific public variables.

For related material, see Vite environment variables and modes.