DEV Community

Cover image for Deploy Next.js 15 to Vercel Without Environment Variable
Mahdi BEN RHOUMA
Mahdi BEN RHOUMA

Posted on • Originally published at iloveblogs.blog

Deploy Next.js 15 to Vercel Without Environment Variable

Deploy Next.js 15 to Vercel Without Environment Variable

Deploy Next.js 15 to Vercel Without Environment Variable Errors

Environment variable errors are the #1 cause of failed Vercel deployments. Your app works locally, but in production you get "undefined" errors, API calls fail, or the build crashes. This guide covers everything you need to know about environment variables in Next.js 15 and Vercel.

This article is part of our comprehensive Deploying Next.js + Supabase to Production guide.

Understanding Environment Variables in Next.js

Next.js has specific rules for environment variables:

  1. NEXT_PUBLIC_* - Exposed to browser (client-side)
  2. Regular variables - Server-side only
  3. Build-time - Bundled during build
  4. Runtime - Loaded when server starts

Common Error Messages

## Error 1: Undefined variable
ReferenceError: process is not defined

## Error 2: Variable not found
Error: NEXT_PUBLIC_API_URL is not defined

## Error 3: Build failure
Error: Environment variable DATABASE_URL is required

## Error 4: Wrong value in production
API_URL: undefined (expected: https://api.example.com)
Enter fullscreen mode Exit fullscreen mode

Solution 1: Understand NEXT_PUBLIC_ Prefix

The most important rule:

Client-Side Variables MUST Have NEXT_PUBLIC_

## .env.local

## ❌ WRONG: Won't work in browser
API_URL=https://api.example.com

## ✅ CORRECT: Accessible in browser
NEXT_PUBLIC_API_URL=https://api.example.com

## ✅ CORRECT: Server-only (no prefix needed)
DATABASE_URL=postgresql://...
API_SECRET_KEY=secret123
Enter fullscreen mode Exit fullscreen mode

Usage in Code

// ❌ BAD: Won't work in client components
'use client'

export function ApiClient() {
  const apiUrl = process.env.API_URL  // ❌ undefined in browser
  return <div>{apiUrl}</div>
}

// ✅ GOOD: Use NEXT_PUBLIC_ prefix
'use client'

export function ApiClient() {
  const apiUrl = process.env.NEXT_PUBLIC_API_URL  // ✅ Works!
  return <div>{apiUrl}</div>
}

// ✅ GOOD: Server-only variables in Server Components
export async function ServerComponent() {
  const dbUrl = process.env.DATABASE_URL  // ✅ Works (server-side)
  // Use dbUrl for database connection
}
Enter fullscreen mode Exit fullscreen mode

Solution 2: Configure Vercel Environment Variables

Step-by-step Vercel configuration:

Add Variables in Vercel Dashboard

## 1. Go to: Vercel Dashboard > Your Project > Settings > Environment Variables

## 2. Add each variable:
Name: NEXT_PUBLIC_SUPABASE_URL
Value: https://your-project.supabase.co
Environment: Production, Preview, Development

Name: NEXT_PUBLIC_SUPABASE_ANON_KEY
Value: your-anon-key
Environment: Production, Preview, Development

Name: DATABASE_URL
Value: postgresql://...
Environment: Production (server-only, don't expose)

## 3. Click "Save"
## 4. Redeploy your application
Enter fullscreen mode Exit fullscreen mode

Environment-Specific Variables

## Production only
NEXT_PUBLIC_API_URL=https://api.production.com
DATABASE_URL=postgresql://prod-db

## Preview (branch deployments)
NEXT_PUBLIC_API_URL=https://api.staging.com
DATABASE_URL=postgresql://staging-db

## Development (local)
NEXT_PUBLIC_API_URL=http://localhost:3000
DATABASE_URL=postgresql://localhost:5432
Enter fullscreen mode Exit fullscreen mode

Solution 3: Use .env Files Correctly

Local development setup:

File Priority Order

## Next.js loads env files in this order (later overrides earlier):
1. .env                    # All environments
2. .env.local              # Local overrides (gitignored)
3. .env.development        # Development only
4. .env.development.local  # Local dev overrides
5. .env.production         # Production only
6. .env.production.local   # Local prod overrides
Enter fullscreen mode Exit fullscreen mode

Example .env Files

## .env (committed to git)
NEXT_PUBLIC_APP_NAME=My App
NEXT_PUBLIC_API_VERSION=v1

## .env.local (gitignored - your local secrets)
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-key
DATABASE_URL=postgresql://localhost:5432/mydb

## .env.production (committed - production defaults)
NEXT_PUBLIC_API_URL=https://api.production.com
NODE_ENV=production
Enter fullscreen mode Exit fullscreen mode

.gitignore Configuration

## .gitignore
.env*.local
.env.local
.env.development.local
.env.test.local
.env.production.local
Enter fullscreen mode Exit fullscreen mode

Solution 4: Handle Build-Time vs Runtime

Critical difference:

Build-Time Variables

// These are bundled at BUILD time
const apiUrl = process.env.NEXT_PUBLIC_API_URL

// If you change the variable after build, it won't update!
// You must rebuild the application
Enter fullscreen mode Exit fullscreen mode

Runtime Variables (Server-Only)

// Server-side variables are loaded at RUNTIME
export async function GET() {
  const dbUrl = process.env.DATABASE_URL  // ✅ Loaded at runtime
  // Connect to database
}
Enter fullscreen mode Exit fullscreen mode

Solution: Use Runtime Configuration

// next.config.mjs
const nextConfig = {
  // For runtime environment variables
  serverRuntimeConfig: {
    // Server-side only
    databaseUrl: process.env.DATABASE_URL,
  },
  publicRuntimeConfig: {
    // Available on both server and client
    apiUrl: process.env.NEXT_PUBLIC_API_URL,
  },
}

// Usage
import getConfig from 'next/config'

const { serverRuntimeConfig, publicRuntimeConfig } = getConfig()
const { databaseUrl } = serverRuntimeConfig
const { apiUrl } = publicRuntimeConfig
Enter fullscreen mode Exit fullscreen mode

Solution 5: Validate Environment Variables

Catch missing variables early:

Create Validation Schema

// lib/env.ts
import { z } from 'zod'

const envSchema = z.object({
  // Client-side (NEXT_PUBLIC_)
  NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
  NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1),
  NEXT_PUBLIC_API_URL: z.string().url(),

  // Server-side
  DATABASE_URL: z.string().url(),
  API_SECRET_KEY: z.string().min(32),
  SMTP_HOST: z.string().optional(),
})

export const env = envSchema.parse({
  NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL,
  NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
  NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
  DATABASE_URL: process.env.DATABASE_URL,
  API_SECRET_KEY: process.env.API_SECRET_KEY,
  SMTP_HOST: process.env.SMTP_HOST,
})

// Usage
import { env } from '@/lib/env'

const apiUrl = env.NEXT_PUBLIC_API_URL  // ✅ Type-safe and validated
Enter fullscreen mode Exit fullscreen mode

Build-Time Validation

// scripts/validate-env.js
const requiredEnvVars = [
  'NEXT_PUBLIC_SUPABASE_URL',
  'NEXT_PUBLIC_SUPABASE_ANON_KEY',
  'DATABASE_URL',
]

const missing = requiredEnvVars.filter(key => !process.env[key])

if (missing.length > 0) {
  console.error('Missing required environment variables:')
  missing.forEach(key => console.error(`  - ${key}`))
  process.exit(1)
}

console.log('✅ All required environment variables are set')
Enter fullscreen mode Exit fullscreen mode
// package.json
{
  "scripts": {
    "validate-env": "node scripts/validate-env.js",
    "build": "npm run validate-env && next build"
  }
}
Enter fullscreen mode Exit fullscreen mode

Solution 6: Use Vercel CLI for Testing

Test locally with production environment:

Install Vercel CLI

npm install -g vercel

## Login
vercel login

## Link project
vercel link
Enter fullscreen mode Exit fullscreen mode

Pull Environment Variables

## Download production env vars to .env.local
vercel env pull .env.local

## Now your local environment matches production
npm run dev
Enter fullscreen mode Exit fullscreen mode

Test Production Build

## Build with production env vars
vercel build

## Test production build locally
vercel dev --prod
Enter fullscreen mode Exit fullscreen mode

Solution 7: Handle Different Environments

Manage multiple environments:

Environment-Specific Configuration

// lib/config.ts
const config = {
  development: {
    apiUrl: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000',
    debug: true,
  },
  production: {
    apiUrl: process.env.NEXT_PUBLIC_API_URL,
    debug: false,
  },
  preview: {
    apiUrl: process.env.NEXT_PUBLIC_API_URL,
    debug: true,
  },
}

const env = process.env.NODE_ENV || 'development'
export default config[env]
Enter fullscreen mode Exit fullscreen mode

Vercel Environment Detection

// Detect Vercel environment
const isProduction = process.env.VERCEL_ENV === 'production'
const isPreview = process.env.VERCEL_ENV === 'preview'
const isDevelopment = process.env.VERCEL_ENV === 'development'

// Use Vercel URL
const vercelUrl = process.env.VERCEL_URL
const siteUrl = isProduction 
  ? 'https://iloveblog.blog'
  : `https://${vercelUrl}`
Enter fullscreen mode Exit fullscreen mode

Common Mistakes

  • Mistake #1: Forgetting NEXT_PUBLIC_ prefix - Client-side variables must have this prefix

  • Mistake #2: Not redeploying after adding variables - Vercel needs redeploy to pick up new variables

  • Mistake #3: Committing .env.local - This file should be gitignored

  • Mistake #4: Using wrong environment - Check which environment (Production/Preview/Development) you're configuring

  • Mistake #5: Exposing secrets - Never use NEXT_PUBLIC_ for sensitive data

FAQ

Why are my environment variables undefined in production?

Either they're not configured in Vercel dashboard, or you forgot the NEXT_PUBLIC_ prefix for client-side variables. Check both.

Do I need to redeploy after adding environment variables?

Yes! Vercel doesn't automatically redeploy. Go to Deployments > Latest > Redeploy.

Can I use environment variables in next.config.js?

Yes, but they must be available at build time. Use process.env.VARIABLE_NAME.

How do I hide sensitive data from the client?

Don't use NEXT_PUBLIC_ prefix. Keep sensitive variables server-side only (API routes, Server Components).

Why do variables work locally but not on Vercel?

Local variables are in .env.local (gitignored). You must add them to Vercel dashboard separately.

Related Articles

Conclusion

Environment variable errors on Vercel are usually caused by missing NEXT_PUBLIC_ prefix for client-side variables, or forgetting to configure variables in the Vercel dashboard. The key rules are:

  1. Use NEXT_PUBLIC_ prefix for browser-accessible variables
  2. Configure all variables in Vercel dashboard
  3. Redeploy after adding/changing variables
  4. Never commit .env.local to git
  5. Validate environment variables at build time

Use the Vercel CLI to pull production environment variables locally for testing. Always validate required variables before deployment to catch issues early.

Remember: NEXT_PUBLIC_ variables are bundled into your JavaScript and visible to users. Never use this prefix for secrets, API keys, or sensitive data.


Originally published at https://www.iloveblogs.blog

Top comments (0)