Environment Variables
Configure environment variables
Environment variables are validated using Zod schemas with separate files for server and client variables.
Structure
src/env/
├── env-server.ts # Server-side environment variables
└── env-client.ts # Client-side environment variables (NEXT_PUBLIC_*)Server-Side Variables
Defined in src/env/env-server.ts:
import { z } from "zod";
const envSchema = z.object({
DATABASE_URL: z.string().url(),
NEXTAUTH_SECRET: z.string().min(1),
STRIPE_SECRET_KEY: z.string().min(1),
// Add more server-side variables...
});
export const env = envSchema.parse(process.env);Usage
import { env } from "@/env/env-server";
const dbUrl = env.DATABASE_URL;
const stripeKey = env.STRIPE_SECRET_KEY;Server Only
These variables are only available on the server. Do not try to access them in client components.
Client-Side Variables
Defined in src/env/env-client.ts:
import { z } from "zod";
const envSchema = z.object({
NEXT_PUBLIC_API_URL: z.string().url(),
NEXT_PUBLIC_APP_NAME: z.string().min(1),
NEXT_PUBLIC_ENABLE_ANALYTICS: z.coerce.boolean(),
// Add more client-side variables...
});
export const env = envSchema.parse({
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
NEXT_PUBLIC_APP_NAME: process.env.NEXT_PUBLIC_APP_NAME,
NEXT_PUBLIC_ENABLE_ANALYTICS: process.env.NEXT_PUBLIC_ENABLE_ANALYTICS,
});Usage
"use client";
import { env } from "@/env/env-client";
const apiUrl = env.NEXT_PUBLIC_API_URL;
const appName = env.NEXT_PUBLIC_APP_NAME;Client variables must be prefixed with NEXT_PUBLIC_ to be accessible in the browser.
Environment Files
Create a .env.local file in the project root:
# Database
DATABASE_URL="postgresql://user:password@localhost:5432/db"
# Authentication
NEXTAUTH_SECRET="your-secret-key"
BETTER_AUTH_SECRET="your-better-auth-secret"
# Stripe
STRIPE_SECRET_KEY="sk_test_..."
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_test_..."
# App Config
NEXT_PUBLIC_API_URL="http://localhost:3000"
NEXT_PUBLIC_APP_NAME="My App"File Priority
| File | Purpose | Commit? |
|---|---|---|
.env.local | Local development overrides | No |
.env | Default values | Yes |
.env.production | Production defaults | Yes |
.env.development | Development defaults | Yes |
Validation
Type Safety
Environment variables are fully type-safe:
import { env } from "@/env/env-server";
env.DATABASE_URL; // ✅ string (validated URL)
env.INVALID_KEY; // ❌ TypeScript errorRuntime Validation
Invalid environment variables cause the app to fail at startup:
Error: Invalid environment variables:
- DATABASE_URL: Invalid url
- STRIPE_SECRET_KEY: RequiredAdding New Variables
Step 1: Choose server or client
Server-side: env-server.ts
Client-side: env-client.ts (must prefix with NEXT_PUBLIC_)
Step 2: Add to schema
const envSchema = z.object({
// ... existing variables
NEW_VARIABLE: z.string().min(1),
});Step 3: Add to .env.local
NEW_VARIABLE="value"Step 4: Use in code
import { env } from "@/env/env-server";
const value = env.NEW_VARIABLE;Common Patterns
URLs
DATABASE_URL: z.string().url()
NEXT_PUBLIC_API_URL: z.string().url()Booleans
ENABLE_FEATURE: z.coerce.boolean()
NEXT_PUBLIC_ANALYTICS: z.coerce.boolean()Numbers
PORT: z.coerce.number()
MAX_UPLOAD_SIZE: z.coerce.number()Enums
NODE_ENV: z.enum(["development", "production", "test"])
LOG_LEVEL: z.enum(["debug", "info", "warn", "error"])Optional with Defaults
PORT: z.coerce.number().default(3000)
ENABLE_CACHE: z.coerce.boolean().default(true)Vercel Deployment
Add environment variables in the Vercel dashboard:
- Go to Project Settings → Environment Variables
- Add each variable for Production, Preview, and Development
- Redeploy to apply changes
Never commit .env.local to version control. Use .env.example to document required variables.
Example .env.example
Create an .env.example file for documentation:
# Database
DATABASE_URL="postgresql://user:password@localhost:5432/db"
# Authentication
NEXTAUTH_SECRET="generate-a-secret-key"
BETTER_AUTH_SECRET="generate-another-secret-key"
# Stripe
STRIPE_SECRET_KEY="sk_test_..."
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_test_..."
# Optional: Analytics
# NEXT_PUBLIC_GA_ID=""Best Practices
- Validate everything - Always add Zod validation for new environment variables
- Use type coercion - Use
z.coercefor numbers and booleans - Provide defaults - Use
.default()for optional variables - Document in .env.example - Keep the example file up to date
- Never commit secrets - Keep
.env.localin.gitignore