Environment Configs

Managing environment variables in Next.js applications

🔐 What are Environment Variables?

Environment variables store configuration values like API keys, database URLs, and secrets outside your code. Next.js provides built-in support for environment variables, keeping sensitive data secure and allowing different configurations for development and production.


# .env.local
DATABASE_URL="postgresql://localhost:5432/mydb"
API_KEY="your-secret-key"
                                    

Environment File Types

🔒

.env.local

Local development secrets

Not in Git All Environments Overrides Others
🛠️

.env.development

Development environment only

Dev Server Can Commit Team Shared
🚀

.env.production

Production environment only

Build Time Can Commit Public Defaults
🌍

.env

Default for all environments

Base Config Can Commit Fallback Values

🔹 Creating Environment Files

Create environment files in your project root. Next.js automatically loads these files based on the current environment. Use .env.local for secrets that should never be committed to version control.

# .env.local (for secrets - add to .gitignore)
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
API_SECRET_KEY="super-secret-key-12345"
STRIPE_SECRET_KEY="sk_test_..."

# .env.development (can be committed)
NEXT_PUBLIC_API_URL="http://localhost:3000/api"
NEXT_PUBLIC_SITE_NAME="My App (Dev)"

# .env.production (can be committed)
NEXT_PUBLIC_API_URL="https://api.myapp.com"
NEXT_PUBLIC_SITE_NAME="My App"

File Priority (highest to lowest):

  1. .env.local
  2. .env.development or .env.production
  3. .env

🔹 Server-Side Variables

Server-side environment variables are only accessible in server components, API routes, and server actions. They're never exposed to the browser, making them perfect for sensitive data like database credentials and API secrets.

// app/api/users/route.ts
export async function GET() {
  // Access server-only variable
  const dbUrl = process.env.DATABASE_URL
  
  // This is secure - never sent to client
  const apiKey = process.env.API_SECRET_KEY
  
  return Response.json({ message: 'Success' })
}

// app/actions.ts (Server Action)
'use server'

export async function createUser(formData: FormData) {
  const db = process.env.DATABASE_URL
  // Use database connection
}

🔹 Client-Side Variables

Expose variables to the browser by prefixing with NEXT_PUBLIC_. These are embedded in the JavaScript bundle at build time, so only use them for non-sensitive data like API endpoints or feature flags.

# .env.local
NEXT_PUBLIC_API_URL="https://api.example.com"
NEXT_PUBLIC_ANALYTICS_ID="GA-123456"
NEXT_PUBLIC_SITE_NAME="My Awesome App"
// app/components/header.tsx
'use client'

export default function Header() {
  // Access in client component
  const siteName = process.env.NEXT_PUBLIC_SITE_NAME
  const apiUrl = process.env.NEXT_PUBLIC_API_URL
  
  return (
    <header>
      <h1>{siteName}</h1>
      <p>API: {apiUrl}</p>
    </header>
  )
}

⚠️ Important:

  • NEXT_PUBLIC_ variables are visible in browser
  • Never put secrets in NEXT_PUBLIC_ variables
  • They're embedded at build time, not runtime

🔹 Loading Environment Variables

Next.js automatically loads environment variables from .env files. You can also load them programmatically or use different files for testing. Variables are available immediately in your application code.

// next.config.js
module.exports = {
  env: {
    // Custom environment variables
    CUSTOM_KEY: process.env.CUSTOM_KEY,
  },
  // Or use publicRuntimeConfig for runtime values
  publicRuntimeConfig: {
    apiUrl: process.env.API_URL,
  },
  serverRuntimeConfig: {
    secretKey: process.env.SECRET_KEY,
  },
}

🔹 Default Values and Validation

Provide fallback values and validate required environment variables at startup. This prevents runtime errors and makes configuration requirements clear. Use a validation library or custom checks to ensure all needed variables are set.

// lib/env.ts
const requiredEnvVars = [
  'DATABASE_URL',
  'API_SECRET_KEY',
] as const

// Validate environment variables
requiredEnvVars.forEach((envVar) => {
  if (!process.env[envVar]) {
    throw new Error(`Missing required environment variable: ${envVar}`)
  }
})

// Export typed environment variables
export const env = {
  databaseUrl: process.env.DATABASE_URL!,
  apiSecretKey: process.env.API_SECRET_KEY!,
  apiUrl: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000',
  nodeEnv: process.env.NODE_ENV || 'development',
} as const
// Use in your app
import { env } from '@/lib/env'

// TypeScript knows these exist
const db = env.databaseUrl
const apiKey = env.apiSecretKey

🔹 Best Practices

Follow these guidelines to manage environment variables securely and effectively:

Security & Organization:

  • Never commit secrets: Add .env.local to .gitignore
  • Use NEXT_PUBLIC_ carefully: Only for non-sensitive data
  • Document variables: Create .env.example with dummy values
  • Validate on startup: Check required variables exist
  • Use different values: Separate dev/staging/production configs
  • Rotate secrets regularly: Update API keys periodically
# .env.example (commit this)
DATABASE_URL="postgresql://user:password@localhost:5432/dbname"
API_SECRET_KEY="your-secret-key-here"
NEXT_PUBLIC_API_URL="https://api.example.com"

# Instructions for team:
# 1. Copy this file to .env.local
# 2. Replace values with your actual credentials
# 3. Never commit .env.local

🧠 Test Your Knowledge

Which prefix makes environment variables accessible in the browser?