Topic 14 of 47
Environment Variables & Config
Overview
Next.js has a structured system for environment variables — separating server-only secrets from client-exposed values. next.config.js controls build behavior, headers, redirects, and more.
Syntax
typescript
// .env.local (git-ignored — local dev)
DATABASE_URL=postgresql://localhost:5432/mydb
NEXTAUTH_SECRET=your-secret-key
GOOGLE_CLIENT_ID=xxx
// Variables MUST be prefixed NEXT_PUBLIC_ to be exposed to browser
NEXT_PUBLIC_API_URL=https://api.example.com
NEXT_PUBLIC_STRIPE_KEY=pk_test_xxx
// Usage
// Server-side: any env var works
const dbUrl = process.env.DATABASE_URL;
// Client-side: ONLY NEXT_PUBLIC_ vars available
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
// next.config.ts
const config = {
images: {
remotePatterns: [{ hostname: "images.example.com" }],
},
async redirects() {
return [{ source: "/old", destination: "/new", permanent: true }];
},
async headers() {
return [{ source: "/(.*)", headers: [{ key: "X-Frame-Options", value: "DENY" }] }];
},
};Common Pitfalls
- Never access process.env in a client component without NEXT_PUBLIC_ prefix — it will be undefined in the browser.
- NEXT_PUBLIC_ variables are baked into the JavaScript bundle at build time — they are visible to end users.
- Interview tip: Validate all required environment variables at startup using Zod — this fails fast with a clear error rather than cryptic runtime crashes.
Real-World Example
Type-safe environment validation with Zod
example
typescript
// lib/env.ts — validate env at startup
import { z } from "zod";
const envSchema = z.object({
DATABASE_URL: z.string().url(),
NEXTAUTH_SECRET: z.string().min(32),
GOOGLE_CLIENT_ID: z.string(),
GOOGLE_CLIENT_SECRET: z.string(),
NEXT_PUBLIC_API_URL: z.string().url(),
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
});
// Parse and validate — throws clear error if missing
export const env = envSchema.parse(process.env);
// Usage
import { env } from "@/lib/env";
const db = new Client({ connectionString: env.DATABASE_URL });