Using Vite’s VITE_ Prefixed Environment Variables in Client Code
Learn how Vite exposes only VITE_ prefixed environment variables to client code via import.meta.env, with a worked example, limits, and common pitfalls.
06 Jul 2025, 04:30 UTC

Quick answer
Vite automatically makes any environment variable whose name starts with VITE_ available in the browser as import.meta.env.VITE_*. The values are replaced at build time, so they are bundled into the final JavaScript and never expose variables without the prefix.
How it works
When Vite starts its dev server or runs vite build, it loads .env, .env.[mode] files and merges them with process.env. Then the internal define plugin walks through the source and replaces every occurrence of import.meta.env.VITE_ with the corresponding string literal. Because the replacement happens during the transform step, the resulting bundle contains the raw value, not a runtime lookup.
Worked example
- Create a file named
.envin the project root: - In a Vue component or any JavaScript file, read the variable:
- Start the dev server:
- To see the replacement in the production build, run:
VITE_GREETING=Hello from Vite
OTHER_SECRET=should_not_be_exposed
export default {
setup() {
console.log(import.meta.env.VITE_GREETING);
// OTHER_SECRET is not available; accessing it yields undefined
}
};
npm run dev // or vite
The browser console shows “Hello from Vite”.
npm run build // vite build
Open the generated asset (e.g., dist/assets/index.-.js) and search for the string Hello from Vite. It appears directly, confirming the compile‑time substitution.
Version consistency
This behavior has been present since Vite 2.x and is expected to remain unchanged in Vite 5. The core define plugin that performs the replacement is part of Vite’s internal pipeline, so unless a major plugin API change alters how define works, the VITE_ prefix rule stays stable.
Limits and safety
- Only variables prefixed with
VITE_are exposed. Anything else stays in the Node process and is replaced withundefinedin the client bundle, preventing accidental secret leakage. - The substitution is static; changing a
.envfile while the dev server is running does not update the value until the server restarts. - If you need runtime‑dependent values (e.g., from a server), you must fetch them via an API; Vite’s env replacement cannot provide them.
- Values are inserted as raw strings. If you want JSON or boolean values, you must stringify them yourself or use a plugin that transforms them.
Common mistakes
- Omitting the
VITE_prefix – results inundefinedbecause Vite ignores the variable. - Accessing
process.env.VITE_GREETINGin the browser –processis not available unless you polyfill it, leading to ReferenceError. - Expecting changes to
.envto hot‑reload – Vite’s dev server only watches source files; env changes require a restart. - Using the variable inside a dynamic import string (e.g.,
import(`./locales/${import.meta.env.VITE_LOCALE}.json`)) – the static replace works, but if the value is undefined the path breaks; always ensure the variable is defined. - Assuming the replacement works in SSR contexts without additional configuration – Vite’s SSR build treats
import.meta.envdifferently, and you may need to expose variables manually.
Practical verification
After setting up the example above, you can confirm the behavior with these steps:
- Run
viteand open the browser console; you should see the expected string. - Run
vite build, inspect the emitted JavaScript file, and verify that the string appears literally. - Try logging
import.meta.env.OTHER_SECRET; it will printundefined, showing the non‑prefixed variable is hidden. - Restart the dev server after editing
.envto see the new value take effect.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.