Welcome to Day 10 of the React Native Advent Calendar!
Today we’re tackling environment variables in Expo - a crucial topic that’s easy to get wrong. Learn how to use .env files properly and avoid the number one security mistake: exposing secrets in your frontend code!
Environment Variables Made Easy
Expo makes environment variables simple with automatic .env file support. Just create a .env file and prefix your variables with EXPO_PUBLIC_:
# Your .env file
EXPO_PUBLIC_API_URL=https://api.example.com
EXPO_PUBLIC_APP_NAME=My Awesome App
EXPO_PUBLIC_FEATURE_FLAG=trueAccess them in your code using process.env:
export default function Home() {
const apiUrl = process.env.EXPO_PUBLIC_API_URL;
const appName = process.env.EXPO_PUBLIC_APP_NAME;
return (
<View>
<Text>Welcome to {appName}</Text>
<Text>API: {apiUrl}</Text>
</View>
);
}That’s it! Expo automatically loads and inlines these values at build time. You can even update them on the fly by now!
The Security Rule You Must Know
Here’s the critical part that trips up many developers:
EXPO_PUBLIC variables are NOT secret!
They’re embedded directly in your JavaScript bundle, which means anyone can read them. Even if you obfuscate your code, with enough criminal intent, they can be decrypted.
What NOT to Store
# ❌ DANGEROUS - Never do this!
EXPO_PUBLIC_API_KEY=sk_live_abc123secretkey
EXPO_PUBLIC_DATABASE_PASSWORD=mypassword123
EXPO_PUBLIC_STRIPE_SECRET_KEY=sk_test_xyz789
EXPO_PUBLIC_OPENAI_API_KEY=sk-proj-abc123Why is this bad? Anyone can:
- Download your app
- Extract the JavaScript bundle
- Search for
EXPO_PUBLIC_variables - Steal your API keys and secrets
The damage: Unauthorized API usage, data breaches, massive bills, and security incidents.
What’s Safe to Store
# ✅ Safe - Public information
EXPO_PUBLIC_API_URL=https://api.example.com
EXPO_PUBLIC_APP_VERSION=1.0.0
EXPO_PUBLIC_ENVIRONMENT=production
EXPO_PUBLIC_FEATURE_ANALYTICS=true
EXPO_PUBLIC_SENTRY_DSN=https://public@sentry.io/123Rule of thumb: If it’s okay for users to see it, it’s safe for EXPO_PUBLIC_. If not, it doesn’t belong in your frontend code!
Safe Secrets: API Routes to the Rescue
For actual secrets, use API routes with Expo Router. Variables without EXPO_PUBLIC_ are safe here because they run server-side:
# Server-side secrets (no EXPO_PUBLIC_ prefix)
OPENAI_API_KEY=sk-proj-abc123
DATABASE_URL=postgresql://user:pass@host/db
STRIPE_SECRET_KEY=sk_live_xyz789
# Client-side public values
EXPO_PUBLIC_API_URL=https://api.example.comUse them in API routes:
export async function POST(request: Request) {
// ✅ Safe - runs server-side only
const apiKey = process.env.OPENAI_API_KEY;
const response = await fetch('https://api.openai.com/v1/completions', {
headers: {
Authorization: `Bearer ${apiKey}`
}
});
return Response.json(await response.json());
}Your frontend calls this API route instead of calling OpenAI directly - keeping your key safe on the server!
export default function Home() {
const generateText = async () => {
// ✅ Calls your API route, not OpenAI directly
const response = await fetch(`${process.env.EXPO_PUBLIC_API_URL}/api/generate`, {
method: 'POST'
});
const data = await response.json();
};
return <Button title="Generate" onPress={generateText} />;
}Result: Your OpenAI key never leaves the server. Users can’t steal it!
Multiple Environments
Expo supports standard .env file precedence for different environments:
.env # Default for all environments
.env.local # Local overrides (add to .gitignore!)
.env.development # Development only
.env.production # Production onlyExample setup:
EXPO_PUBLIC_API_URL=http://localhost:3000
EXPO_PUBLIC_ENVIRONMENT=developmentEXPO_PUBLIC_API_URL=https://api.myapp.com
EXPO_PUBLIC_ENVIRONMENT=productionPro tip: Add .env.local to .gitignore so each developer can have their own local overrides!
EAS Build: Upload Your Secrets
When building with EAS, you can upload environment variables to EAS servers instead of committing them to your repo. This is perfect for team collaboration and CI/CD!
Set Environment Variables on EAS
# Create a secret on EAS
eas secret:create --name OPENAI_API_KEY --value sk-proj-abc123 --type string
# Or upload from your local .env
eas env:push
# Pull secrets from EAS for local development
eas env:pullConfigure in eas.json
{
"build": {
"production": {
"env": {
"EXPO_PUBLIC_API_URL": "https://api.myapp.com",
"EXPO_PUBLIC_ENVIRONMENT": "production"
}
},
"development": {
"env": {
"EXPO_PUBLIC_API_URL": "https://staging.myapp.com",
"EXPO_PUBLIC_ENVIRONMENT": "staging"
}
}
}
}Benefits:
- Secrets never committed to git
- Different values per build profile
- Team members can share secrets via EAS
- Automatic injection during EAS builds
Important: EAS secrets are still inlined if they use EXPO_PUBLIC_ prefix! Use them for build-time secrets or server-side API routes.
Wrapping Up
Environment variables in Expo are powerful but require careful handling. Remember the golden rule: EXPO_PUBLIC = Public Information.
Quick recap:
- Use
.envfiles withEXPO_PUBLIC_prefix for public config - Never put secrets in
EXPO_PUBLIC_variables - Use API routes for server-side secrets (no
EXPO_PUBLIC_prefix) - Upload secrets to EAS for builds instead of committing them
- Use static references only:
process.env.EXPO_PUBLIC_KEY
Want to dive deeper?
- Expo Environment Variables Guide
- EAS Environment Variables
- What Every Dev Should Know About Environment Variables
Found API keys in production code? It happens to the best of us - rotate them immediately and move to API routes!
Tomorrow we’ll explore Day 11’s topic - see you then! 🎄