Beginner10 min1 prerequisite

Manage environment variables securely in Vercel for different environments and integrations.

Environment Variables

Environment variables store secrets and configuration that differ between development, preview, and production.

Understanding Environments

Vercel has three environments:

EnvironmentBranchUse Case
ProductionmainLive site
Previewfeature branchesTesting PRs
DevelopmentLocalLocal dev

Each can have different variable values.

Adding Variables

Via Dashboard

Terminal
Project  Settings  Environment Variables

Name: SUPABASE_SERVICE_ROLE_KEY
Value: eyJ...
Environments:  Production  Preview  Development

Via CLI

Terminal
# Add to all environments
vercel env add VARIABLE_NAME

# Add to specific environment
vercel env add VARIABLE_NAME production

# Pull to local .env.local
vercel env pull .env.local

Via vercel.json

For non-sensitive build-time variables:

Terminal
{
  "build": {
    "env": {
      "NEXT_PUBLIC_APP_NAME": "My App"
    }
  }
}

Variable Types

Server-Side Only (Secrets)

Never exposed to browser:

Terminal
# Database credentials
DATABASE_URL=postgresql://...

# API keys
SUPABASE_SERVICE_ROLE_KEY=eyJ...

# Third-party secrets
STRIPE_SECRET_KEY=sk_live_...
OPENAI_API_KEY=sk-...

Access in Server Components and API routes:

Terminal
// app/api/route.ts
export async function GET() {
  const apiKey = process.env.OPENAI_API_KEY
  // Safe - only runs on server
}

Client-Side (Public)

Exposed to browser—prefix with NEXT_PUBLIC_:

Terminal
# Client-safe values
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
NEXT_PUBLIC_APP_URL=https://myapp.com

Access in Client Components:

Terminal
'use client'

function Component() {
  const url = process.env.NEXT_PUBLIC_SUPABASE_URL
  // Available in browser
}

Environment-Specific Values

Different Values Per Environment

Terminal
Production:
  NEXT_PUBLIC_API_URL=https://api.example.com
  DATABASE_URL=postgresql://prod-db...

Preview:
  NEXT_PUBLIC_API_URL=https://staging-api.example.com
  DATABASE_URL=postgresql://staging-db...

Development:
  NEXT_PUBLIC_API_URL=http://localhost:3001
  DATABASE_URL=postgresql://localhost...

Supabase Configuration

Typical setup:

Terminal
# Both environments (same project)
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...

# Production only (server-side)
SUPABASE_SERVICE_ROLE_KEY=eyJ...

# Or separate projects
# Production
NEXT_PUBLIC_SUPABASE_URL=https://prod.supabase.co
# Preview
NEXT_PUBLIC_SUPABASE_URL=https://staging.supabase.co

System Variables

Vercel provides automatic variables:

Terminal
# Always available
VERCEL=1
VERCEL_ENV=production|preview|development
VERCEL_URL=my-app-abc123.vercel.app
VERCEL_GIT_COMMIT_SHA=abc123...
VERCEL_GIT_COMMIT_MESSAGE="feat: add login"
VERCEL_GIT_REPO_SLUG=my-repo

Using System Variables

Terminal
// Get current URL dynamically
function getBaseUrl() {
  if (process.env.VERCEL_URL) {
    return `https://${process.env.VERCEL_URL}`
  }
  return 'http://localhost:3000'
}

// Check environment
if (process.env.VERCEL_ENV === 'production') {
  // Production-only code
}

Integration Variables

Connecting Services

Vercel Integrations auto-add variables:

Terminal
Marketplace  Supabase  Connect

Auto-added:
- SUPABASE_URL
- SUPABASE_ANON_KEY
- SUPABASE_SERVICE_ROLE_KEY
- POSTGRES_URL

Popular Integrations

ServiceVariables Added
SupabaseSUPABASE_URL, keys
PlanetScaleDATABASE_URL
Vercel KVKV_URL, KV_TOKEN
Vercel BlobBLOB_READ_WRITE_TOKEN

Local Development

.env.local

Create .env.local for local development:

Terminal
# .env.local (never commit!)
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
SUPABASE_SERVICE_ROLE_KEY=eyJ...

Pull from Vercel

Sync production variables locally:

Terminal
# Pull to .env.local
vercel env pull .env.local

# Pull specific environment
vercel env pull .env.local --environment=preview

.env.example

Create .env.example for documentation:

Terminal
# .env.example (commit this!)
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-key

Security Best Practices

Never Expose Secrets

Terminal
// WRONG - exposes secret to client
'use client'
function Component() {
  // This will be undefined (correct behavior)
  const key = process.env.SUPABASE_SERVICE_ROLE_KEY
}

// CORRECT - only in server code
// app/api/admin/route.ts
export async function GET() {
  const key = process.env.SUPABASE_SERVICE_ROLE_KEY
  // Safe - server only
}

gitignore

Terminal
# .gitignore
.env
.env.local
.env.*.local

Rotate Compromised Keys

If a secret is exposed:

  1. Regenerate key in service (Supabase, Stripe, etc.)
  2. Update in Vercel dashboard
  3. Redeploy application
  4. Check logs for unauthorized access

Variable Patterns

Feature Flags

Terminal
NEXT_PUBLIC_FEATURE_NEW_UI=true
NEXT_PUBLIC_FEATURE_BETA_API=false
Terminal
function Component() {
  if (process.env.NEXT_PUBLIC_FEATURE_NEW_UI === 'true') {
    return <NewUI />
  }
  return <OldUI />
}

API Versioning

Terminal
# Production - stable API
API_VERSION=v1

# Preview - test new API
API_VERSION=v2

Debug Mode

Terminal
# Production
DEBUG_MODE=false

# Preview
DEBUG_MODE=true
Terminal
if (process.env.DEBUG_MODE === 'true') {
  console.log('Debug info:', data)
}

Managing Variables

List Variables

Terminal
# Via CLI
vercel env ls

# Output
> Environment Variables for my-project
> NEXT_PUBLIC_SUPABASE_URL (Production, Preview, Development)
> SUPABASE_SERVICE_ROLE_KEY (Production)

Remove Variables

Terminal
# Via CLI
vercel env rm VARIABLE_NAME

Update Variables

Dashboard:

Terminal
Settings  Environment Variables  Edit  Save

Note: Changes require redeployment to take effect.

Troubleshooting

Variable Not Available

  1. Check prefix: Client-side needs NEXT_PUBLIC_
  2. Check environment: Is it enabled for this environment?
  3. Redeploy: Variables update after deployment

Undefined in Browser

Terminal
// If undefined, likely missing NEXT_PUBLIC_ prefix
console.log(process.env.API_KEY)  // undefined in browser
console.log(process.env.NEXT_PUBLIC_API_KEY)  // works

Build vs Runtime

Some variables are only available at build time:

Terminal
// Build time - baked into bundle
const buildTime = process.env.NEXT_PUBLIC_BUILD_ID

// Runtime - for dynamic values, use API route

Summary

  • Server secrets: No prefix, never exposed to browser
  • Client variables: NEXT_PUBLIC_ prefix required
  • Environments: Production, Preview, Development
  • Pull locally: vercel env pull .env.local
  • Never commit: Add .env.local to .gitignore

Next Steps

Learn advanced Vercel features like Edge Functions and Analytics.

Mark this lesson as complete to track your progress