Environment Basics
Environment variables configure your app for different environments (development, staging, production).
Why Environment Variables?
- Security - Keep secrets out of code
- Flexibility - Different configs for different environments
- Portability - Same code, different deployments
Common Environment Variables
# .env.example (commit this file)
# App
NEXT_PUBLIC_APP_URL=http://localhost:3000
NEXT_PUBLIC_APP_NAME=MyApp
# API
API_URL=http://localhost:8000
API_KEY=your-api-key
# Database
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
# Auth
NEXTAUTH_SECRET=random-secret-here
NEXTAUTH_URL=http://localhost:3000
# Third-party services
STRIPE_SECRET_KEY=sk_test_xxx
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxx
# Feature flags
NEXT_PUBLIC_ENABLE_ANALYTICS=falseFile Structure
project/
├── .env # Default (loaded in all environments)
├── .env.local # Local overrides (git ignored)
├── .env.development # Development defaults
├── .env.development.local # Local dev overrides (git ignored)
├── .env.production # Production defaults
├── .env.production.local # Local prod overrides (git ignored)
├── .env.test # Test environment
└── .env.example # Template (commit this)Loading Priority
Variables are loaded in this order (later overrides earlier):
.env(default).env.local(always loaded, git ignored).env.[environment](development/production/test).env.[environment].local(git ignored)
Public vs Private Variables
Client-Side (Public)
# Prefix with NEXT_PUBLIC_ (Next.js) or REACT_APP_ (CRA)
NEXT_PUBLIC_API_URL=https://api.example.com
REACT_APP_API_URL=https://api.example.com// Accessible in browser
const apiUrl = process.env.NEXT_PUBLIC_API_URL;Server-Side Only (Private)
# No prefix - only available on server
DATABASE_URL=postgresql://...
API_SECRET_KEY=xxx// Only works in server code (API routes, getServerSideProps)
const dbUrl = process.env.DATABASE_URL;
// This will be undefined in browser!.gitignore Setup
# Environment files with secrets
.env.local
.env.development.local
.env.production.local
.env.test.local
# Never commit these
.env*.localType Safety
// env.d.ts
declare namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production' | 'test';
NEXT_PUBLIC_APP_URL: string;
NEXT_PUBLIC_API_URL: string;
DATABASE_URL: string;
API_SECRET_KEY: string;
}
}Accessing Variables
In React Components
function ApiStatus() {
// Only NEXT_PUBLIC_ or REACT_APP_ prefixed vars
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
return <div>API: {apiUrl}</div>;
}In Next.js API Routes
// pages/api/users.ts
export default function handler(req, res) {
// All env vars available
const dbUrl = process.env.DATABASE_URL;
const apiKey = process.env.API_SECRET_KEY;
// ...
}In getServerSideProps
export const getServerSideProps = async () => {
// All env vars available
const data = await fetch(process.env.API_URL, {
headers: {
Authorization: `Bearer ${process.env.API_SECRET_KEY}`,
},
});
return { props: { data } };
};Different Environments
Development
# .env.development
NEXT_PUBLIC_API_URL=http://localhost:8000
NEXT_PUBLIC_ENABLE_DEBUG=trueProduction
# .env.production
NEXT_PUBLIC_API_URL=https://api.myapp.com
NEXT_PUBLIC_ENABLE_DEBUG=falseTest
# .env.test
NEXT_PUBLIC_API_URL=http://localhost:8000
DATABASE_URL=postgresql://localhost:5432/myapp_testPlatform-Specific Setup
Vercel
# Set in Vercel dashboard or CLI
vercel env add DATABASE_URL production
vercel env add DATABASE_URL preview
vercel env add DATABASE_URL developmentNetlify
# Set in Netlify dashboard under Site settings > Build & deploy > EnvironmentDocker
# Dockerfile
ENV NODE_ENV=production
# docker-compose.yml
services:
app:
environment:
- DATABASE_URL=postgresql://db:5432/myapp
- API_KEY=${API_KEY} # From host .envSecurity Best Practices
- Never commit secrets - Use
.env.localfor sensitive values - Use .env.example - Document required variables
- Validate at startup - Fail fast if vars are missing
- Rotate secrets regularly - Especially after incidents
- Use secret managers - AWS Secrets Manager, HashiCorp Vault
- Audit access - Know who can see production secrets
Common Mistakes
- Committing .env.local - Add to .gitignore
- Using secrets client-side - Never prefix secrets with NEXT_PUBLIC_
- Hardcoding values - Use env vars for anything that varies
- Missing in production - Always set vars before deploying
- Wrong prefix - NEXT_PUBLIC_ for client, none for server