Next.js Environment Variables

Manage environment variables in Next.js with env files, the NEXT_PUBLIC prefix, and runtime access.

TL;DR

  1. Store secrets in .env.local and read them with process.env.
  2. Prefix a name with NEXT_PUBLIC_ to expose it to the browser.
  3. Restart the dev server after you change any .env file.

Env Files

    .env.local

    Keep local secrets here; this file is ignored by git by default.

    # .env.local  (never commit)
    DATABASE_URL="postgres://localhost:5432/app"
    API_KEY="sk_test_123"
    .env

    Store shared, non-secret defaults that are safe to commit to the repo.

    # .env  (safe to commit)
    NEXT_PUBLIC_SITE_NAME="Acme"
    LOG_LEVEL="info"
    Load Order

    Earlier sources win, so local files override the shared defaults.

    1. process.env          # real environment
    2. .env.development.local
    3. .env.local
    4. .env.development
    5. .env
    $VARIABLE

    Reuse one variable inside another by referencing it with a dollar sign.

    HOST="example.com"
    API_URL="https://$HOST/api"

Server Variables

    process.env

    Read any variable on the server, where secrets stay private.

    const key = process.env.API_KEY;
    
    export async function getWeather() {
      return fetch(`https://api.example.com?key=${key}`);
    }
    Server Only

    Variables without the public prefix are undefined in browser code.

    'use client';
    
    // undefined in the browser
    console.log(process.env.API_KEY);
    Pass as a Prop

    Read a value on the server and pass it down to a Client Component.

    export default function Page() {
      const region = process.env.REGION ?? 'us';
      return <RegionBadge region={region} />;
    }

Public Variables

    NEXT_PUBLIC_

    Add this prefix to make a value available in browser JavaScript.

    # .env
    NEXT_PUBLIC_ANALYTICS_ID="G-12345"
    Read in the Browser

    Access a public variable from a Client Component just like on the server.

    'use client';
    
    export function SiteName() {
      const name = process.env.NEXT_PUBLIC_SITE_NAME;
      return <span>{name}</span>;
    }
    Static Lookup

    Write the full variable name, because dynamic lookups are not replaced at build.

    // Works: replaced at build time
    process.env.NEXT_PUBLIC_API_URL;
    
    // Fails in the browser
    const name = 'NEXT_PUBLIC_API_URL';
    process.env[name];

Validation and Runtime

    z.object()

    Validate required variables once, so a missing value fails with a clear error.

    // lib/env.ts
    import { z } from 'zod';
    
    export const env = z
      .object({
        DATABASE_URL: z.string().url(),
        API_KEY: z.string().min(1),
      })
      .parse(process.env);
    connection()

    Read a variable at request time instead of when the page is prebuilt.

    import { connection } from 'next/server';
    
    export default async function Page() {
      await connection();
      const flag = process.env.FEATURE_FLAG;
      return <p>{flag}</p>;
    }
    loadEnvConfig()

    Load the same env files in scripts and tools that run outside Next.js.

    import { loadEnvConfig } from '@next/env';
    
    loadEnvConfig(process.cwd());

Tips

  1. Validate required variables once at startup with a schema, so a missing DATABASE_URL fails fast with a clear message.
  2. Commit a .env.example file that lists every variable name without values, so teammates know what to set.

Warnings

  1. Any variable starting with NEXT_PUBLIC_ is bundled into browser JavaScript, so never put a secret behind that prefix.
  2. Values with the NEXT_PUBLIC_ prefix are inlined when you run next build, so changing them later requires a new build.

In Practice

FAQ