Skip to main content

Documentation Index

Fetch the complete documentation index at: https://next-safe-env.dev/llms.txt

Use this file to discover all available pages before exploring further.

This guide walks you through installing next-safe-env, writing your first environment schema, importing typed values in your app, and understanding what happens when a variable is missing or invalid. The whole setup takes less than five minutes.
1

Install

Add next-safe-env to your project using your package manager of choice.
npm install next-safe-env
2

Create src/env.ts

Create a single file that defines your entire environment schema. Split your variables into server (never exposed to the browser) and client (safe to use anywhere). Every key in runtimeEnv must reference its corresponding process.env value individually - do not spread the whole process.env object, as bundlers need individual references to inline values statically and keep server vars out of the client bundle.
src/env.ts
import { createEnv, str, url, port, bool } from 'next-safe-env'

export const env = createEnv({
  server: {
    DATABASE_URL: url(),                              // must be a valid URL
    JWT_SECRET:   str().min(32),                      // string, at least 32 chars
    SMTP_PORT:    port().default(587),                // number, defaults to 587
    NODE_ENV:     str().enum(['development', 'production', 'test']),
    REDIS_URL:    url().optional(),                   // valid URL or undefined
  },
  client: {
    NEXT_PUBLIC_API_URL:      url(),
    NEXT_PUBLIC_APP_NAME:     str().default('My App'),
    NEXT_PUBLIC_ENABLE_DEBUG: bool().default(false),
  },
  // Pass each variable individually, not process.env as a whole.
  runtimeEnv: {
    DATABASE_URL:             process.env.DATABASE_URL,
    JWT_SECRET:               process.env.JWT_SECRET,
    SMTP_PORT:                process.env.SMTP_PORT,
    NODE_ENV:                 process.env.NODE_ENV,
    REDIS_URL:                process.env.REDIS_URL,
    NEXT_PUBLIC_API_URL:      process.env.NEXT_PUBLIC_API_URL,
    NEXT_PUBLIC_APP_NAME:     process.env.NEXT_PUBLIC_APP_NAME,
    NEXT_PUBLIC_ENABLE_DEBUG: process.env.NEXT_PUBLIC_ENABLE_DEBUG,
  },
})
The available validators are:
ValidatorOutput typeNotes
str()stringRaw string, no coercion
num()numberCoerces "3000"3000
bool()booleanCoerces "true"/"1"/"yes"/"on"true
url()stringShorthand for str().url()
port()numberShorthand for num().port() - integer 1–65535
All validators are chainable. Common modifiers:
str().min(8).max(64)                                     // length constraints
str().regex(/^[a-z_]+$/)                                 // pattern match
str().enum(['debug', 'info', 'warn', 'error'])           // literal union
num().int().min(1).max(100).default(10)                  // integer with bounds
url().optional()                                         // valid URL or undefined
bool().default(false)                                    // boolean with fallback
3

Use env in your app

Import the env object anywhere in your project. Server vars and client vars are all available on the same object, with types fully inferred from your schema.
app/api/auth/route.ts
import { env } from '@/env'

export async function POST() {
  const db    = await connectDB(env.DATABASE_URL)  // string
  const port  = env.SMTP_PORT                      // number, not string
  const debug = env.NEXT_PUBLIC_ENABLE_DEBUG        // boolean
}
app/components/Header.tsx
import { env } from '@/env'

export function Header() {
  // Client vars are fully typed and available in both server and client components.
  return <h1>{env.NEXT_PUBLIC_APP_NAME}</h1>
}
Server vars carry a ServerOnly<T> brand visible in IDE tooltips, making their origin clear at a glance. For a hard compile error that prevents client components from importing server vars, add import 'server-only' at the top of your env.ts file.
4

What happens on failure

If any variable is missing or invalid, next-safe-env refuses to start the app and prints every problem at once - no hunting for the first undefined at runtime.
[next-safe-env] Environment validation failed - 3 error(s):

  ✗ DATABASE_URL     - Required. Expected a valid URL. Got: "postgres-localhost"
  ✗ JWT_SECRET       - Too short. Must be ≥ 32 characters. Got length: 12
  ✗ SMTP_PORT        - Invalid port. Must be 1–65535. Got: "99999"

  Server vars:  2 valid, 3 invalid
  Client vars:  2 valid, 0 invalid

  Set the correct values in your .env file or deployment environment and restart.
Fix the values in your .env file or deployment environment and restart. The error lists every failing field so you can fix them all in one pass.
next-safe-env validates what is already in process.env. It does not load .env files on its own. Use Next.js built-in .env support or dotenv to load your .env files first, then next-safe-env will validate and type whatever ends up in process.env.

Next steps

Next.js guide

Set up server/client splitting, NEXT_PUBLIC_ prefix enforcement, and the server-only module guard.

Node.js guide

Use next-safe-env in plain Node.js servers, APIs, and CLI scripts.