Full-project lesson · needs Node.js, npm and real project files, so it does not run in the in-page playground.
Different environments need different settings: your local machine talks to a test API, production to the real one. Environment variables keep those settings out of your code, so you do not edit source files to deploy.
Vite and .env files
Vite reads environment variables from files in the project root:
| File | Loaded |
|---|---|
.env | Always |
.env.local | Always, ignored by Git (the template’s .gitignore has *.local) |
.env.development | Only with npm run dev (mode development) |
.env.production | Only with npm run build (mode production) |
.env.[mode].local | A specific mode, local only |
More specific files override less specific ones.
The VITE_ prefix
Only variables that start with VITE_ are exposed to your client code. Everything else stays in Node and is invisible to the browser. This protects you from leaking something by accident, but it also means that anything that is exposed is public; see the warning below.
Create a file named .env in the project root:
VITE_API_URL=https://api.example.com
VITE_APP_NAME=Learning Shop
Read the values in code with import.meta.env:
function App() {
return (
<p>
{import.meta.env.VITE_APP_NAME} talks to {import.meta.env.VITE_API_URL}
</p>
)
}
export default App
This was built with Vite and checked in a browser: the page shows the values from .env, and the built JavaScript file contains the literal text https://api.example.com. That second fact matters; see the warning.
Built-in values
Vite also provides these automatically:
| Value | Meaning |
|---|---|
import.meta.env.MODE | "development" or "production" (or your custom mode) |
import.meta.env.DEV | true in dev |
import.meta.env.PROD | true in a production build |
import.meta.env.BASE_URL | The base path the app is served from |
if (import.meta.env.DEV) {
console.log('Running in development')
}
Values are replaced at build time
During npm run build, Vite replaces each import.meta.env.VITE_X with the literal value. The built files contain the text. To change a value you must rebuild. You cannot change it by editing the server’s environment after the build, unless your app loads a config file at run time.
Everything with the VITE_ prefix is public
VITE_ variable. Use environment variables for configuration (an API base URL, a public analytics id), not for secrets. The next lessons explain what to do with secrets: API Security Fundamentals and Why Secret Keys Must Not Be Exposed.Do not commit secrets, even for public values
Commit a template file such as .env.example that lists the names with fake or empty values, and keep real local values in .env.local. For public configuration, committing .env is fine, but the habit of keeping local overrides in .env.local protects you when someone adds a sensitive value later.
Modes
You can run Vite in any mode and load matching files:
npm run dev # mode: development
npm run build # mode: production
npx vite build --mode staging # loads .env.staging
TypeScript
Declare your variables so import.meta.env is typed. The Vite TypeScript template includes a vite-env.d.ts file where you extend ImportMetaEnv:
interface ImportMetaEnv {
readonly VITE_API_URL: string
readonly VITE_APP_NAME: string
}
Common mistakes
- Forgetting the
VITE_prefix. The value isundefinedin the browser. - Using
process.env. That is Node syntax. In Vite, useimport.meta.env. - Editing
.envwhile the dev server runs. Restart the server to pick up changes. - Expecting runtime configuration. The values are fixed when you build.
- Putting secrets in
VITE_variables, the most serious mistake.
Key points
- Put configuration in
.envfiles and read it withimport.meta.env.VITE_NAME. - Only
VITE_-prefixed variables reach the browser, and they are visible to everyone. - Values are inlined at build time, so rebuild after changing them.