NextjsEnvironment Variables

Environment Variables

Environment variables let you keep configuration — API keys, database URLs, feature flags — out of your source code, and change it between environments (local, staging, production) without touching a single line. Next.js has built-in support for loading variables from .env files and exposing a controlled subset of them to the browser when you explicitly ask for it.
.env.local for secrets
The conventional place for local, machine-specific secrets is .env.local at the project root. Next.js loads it automatically and, critically, it is gitignored by default in every Next.js starter — so secrets never get committed to version control.

.env.local

Bash
DATABASE_URL=postgres://user:password@localhost:5432/mydb
STRIPE_SECRET_KEY=sk_live_xxxxxxxxxxxx
NEXT_PUBLIC_ANALYTICS_ID=G-ABC123XYZ

Reading a variable on the server

TSX
// This code only ever runs on the server (Server Component,
// Route Handler, Server Action), so it is safe to read a secret here.
async function getUser(id: string) {
  const res = await fetch(`${process.env.DATABASE_URL}/users/${id}`)
  return res.json()
}
The NEXT_PUBLIC_ prefix
By default, environment variables are only available on the server — they are stripped out of anything that gets bundled for the browser. To make a variable available in client-side code, prefix its name with NEXT_PUBLIC_. At build time, Next.js inlines the value directly into the client JavaScript bundle wherever that variable is referenced.

A NEXT_PUBLIC_ variable used in a Client Component

TSX
'use client'

export default function Analytics() {
  // Inlined at build time — visible to anyone who opens dev tools.
  const id = process.env.NEXT_PUBLIC_ANALYTICS_ID
  return <span data-analytics-id={id} />
}
NEXT_PUBLIC_ variables are public, full stop
Anything prefixed with NEXT_PUBLIC_ ends up baked into the JavaScript that ships to every visitor's browser. It is readable by anyone who opens dev tools or views the page source. Never put an API secret, private key, or database credential behind that prefix — use it only for values that are already safe to be public, like an analytics ID or a public API base URL.
.env file variants and precedence
Next.js supports several .env file names, loaded in a specific order so more specific files can override more general ones.

File

Loaded when

Committed to git?

Typical contents

.env

Always

Yes

Safe defaults shared by every environment

.env.local

Always (overrides .env)

No (gitignored)

Local secrets, personal overrides

.env.development

Only during next dev

Yes

Dev-specific defaults

.env.production

Only during next build / next start

Yes

Production-specific defaults

.env.development.local / .env.production.local

Matching env only

No (gitignored)

Environment-specific secrets

Note
Precedence, highest to lowest: process.env (already set in the shell) → .env.$(NODE_ENV).local .env.local.env.$(NODE_ENV) .env. A value defined in an earlier (higher-precedence) source wins and is never overwritten by a later file.
Reading variables in code

Accessing process.env

TS
// Server-only: works because this file never runs in the browser
const dbUrl = process.env.DATABASE_URL

// Available on both server and client, because of the prefix
const analyticsId = process.env.NEXT_PUBLIC_ANALYTICS_ID

if (!dbUrl) {
  throw new Error('DATABASE_URL is not set')
}
  • Put secrets in .env.local — it is loaded automatically and gitignored by default.

  • Prefix a variable with NEXT_PUBLIC_ only to intentionally expose it to the browser.

  • Server-only variables (no prefix) are stripped from the client bundle and are safe for API keys.

  • .env, .env.local, .env.development, .env.production (and their .local variants) stack with a defined precedence.

  • Restart next dev after editing an .env file — values are read once at process start.