Environment Variables
We keep one env file per environment, each git-ignored, plus a single committed template:
| File | Environment | In git |
|---|---|---|
.env.dev |
development | ignored |
.env.preview |
preview | ignored |
.env.prod |
production | ignored |
.env.example |
template (dummy values) | committed |
If you’re coming from another stack, preview is our “staging” — the
internal, pre-production environment. We call it preview to match Expo’s EAS
preview build profile and the APP_ENV=preview value.
The variable names are the same across environments — only the values
differ — so one .env.example documents them all. Copy it into each
environment file and fill in the real values:
cp .env.example .env.dev # repeat for .env.preview and .env.prod
Loading the right file
Select the file per environment with
env-cmd (a dev dependency) in your
package.json scripts:
"scripts": {
"start": "env-cmd -f .env.dev expo start",
"start:preview": "env-cmd -f .env.preview expo start",
"start:prod": "env-cmd -f .env.prod expo start"
}
Give each file its own APP_ENV (development / preview / production) so
the runtime values env-cmd loads and the build-time config the dynamic
app.config.ts selects stay in sync from one source. (dotenv-cli works the
same way if you already use it.) For EAS builds the values still come from the
env block of the matching profile in eas.json — see Build-Time Config.
The EXPO_PUBLIC_ prefix
Any variable prefixed with EXPO_PUBLIC_ is inlined into the app bundle at
build time and readable at runtime through process.env:
const apiUrl = process.env.EXPO_PUBLIC_API_URL;
EXPO_PUBLIC_* values ship inside the client app — anyone can read them.
Never put secrets (API keys with write access, tokens, passwords) there. Those
belong on the backend or in EAS secrets. See Environment & Configuration →
Secrets.
What’s committed
.env.dev,.env.preview,.env.prod— your real per-environment values. Git-ignored. Never commit them..env.example— the template with empty or dummy values. Committed, so everyone knows which variables a project needs.
Why one file per environment
- Isolation — a dev or test wallet/credential in
.env.devcan never be bundled into a preview or production build. - No blocked tooling — real wallets and keys live only in git-ignored files, never in tracked content, so Claude Code and secret scanners don’t trip on a detected key and refuse to work.
For the full picture — runtime vs build-time config and how APP_ENV selects an
environment — see Environment & Configuration.