ByteNoteByteNote

字节笔记本

2026年7月19日

Next.js Environment Variables: A Practical Reference

API中转
¥120

Environment variables in Next.js let you configure your app differently across environments (development, staging, production) without changing code. The main thing to understand is the split between server-only variables and client-accessible ones — and that the prefix NEXT_PUBLIC_ is what controls that split.

The NEXT_PUBLIC_ Prefix

Only variables prefixed with NEXT_PUBLIC_ are included in the client-side bundle. Everything else stays on the server.

env
# .env

# Accessible in both server and client code
NEXT_PUBLIC_API_URL=https://api.example.com

# Server-only
DATABASE_URL=postgresql://localhost:5432/mydb
SECRET_KEY=abc123
javascript
// Client component — this works
const apiUrl = process.env.NEXT_PUBLIC_API_URL

// Client component — this will be undefined
const dbUrl = process.env.DATABASE_URL

Environment File Hierarchy

Next.js loads .env files in this order (later files override earlier ones):

  1. .env — defaults shared across all environments
  2. .env.local — local overrides (git-ignored)
  3. .env.development — loaded when NODE_ENV=development
  4. .env.production — loaded when NODE_ENV=production
  5. .env.development.local — local overrides for development
  6. .env.production.local — local overrides for production

Build-Time vs Runtime

A common source of confusion: non-prefixed variables are available at build time for SSG pages, but at runtime for server-rendered pages. If you're doing static generation (output: 'export'), only NEXT_PUBLIC_ variables are available in the output.

TypeScript Support

Create a type definition file for autocomplete and type checking:

typescript
// src/env.d.ts
declare namespace NodeJS {
  export interface ProcessEnv {
    NEXT_PUBLIC_API_URL: string
    NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: string
    DATABASE_URL: string
    CLERK_SECRET_KEY: string
    NODE_ENV: 'development' | 'production' | 'test'
  }
}

Validation with Zod

Validating environment variables at startup catches misconfiguration early:

typescript
import { z } from 'zod'

const envSchema = z.object({
  NEXT_PUBLIC_API_URL: z.string().url(),
  DATABASE_URL: z.string().url(),
  CLERK_SECRET_KEY: z.string().min(1),
})

const env = envSchema.parse(process.env)

Run this at app startup (server-side) so the app fails fast instead of silently using undefined.

Docker

Dockerfile

dockerfile
ENV NEXT_PUBLIC_API_URL=https://api.example.com

Docker Compose

yaml
version: '3'
services:
  app:
    image: my-app
    env_file:
      - .env.production

Runtime Override

bash
docker run -e NEXT_PUBLIC_API_URL=https://staging.example.com my-app

Note that NEXT_PUBLIC_ variables are inlined at build time, so changing them at runtime via docker run -e won't affect client-side code unless you rebuild.

Common Mistakes

  • Committing .env.local — add it to .gitignore immediately.
  • Using process.env.MY_VAR in client code — it'll be undefined without the prefix.
  • Expecting runtime changes to affect static exports — if you used output: 'export', the values are baked in at build time.
  • Forgetting to restart the dev server after changing .env files — Next.js reads them on startup.
分享: