Skip to main content
next-safe-env gives you a single env object where every variable is validated at startup, typed precisely, and automatically split between server and client bundles. Instead of scattering process.env.X calls across your codebase and discovering missing values mid-request, you define your schema once in src/env.ts and get full TypeScript autocomplete everywhere you import it.
1

Install next-safe-env

Add the package to your project. It has zero runtime dependencies.
2

Create src/env.ts

Define your server and client schemas. Pass each process.env reference individually in runtimeEnv - this lets the Next.js bundler inline each value statically and prevents server vars from leaking into the client bundle.
next-safe-env validates what is already in process.env. It does not load .env files. Next.js loads .env files automatically, so no extra setup is needed.
3

Use it in a server context

Import env in any server-side file - API routes, Server Components, server actions. All vars from both schemas are available here.
4

Use it in a client component

Import the same env object in Client Components. Client vars are fully typed and autocomplete. Server vars carry the ServerOnly<T> brand, which is visible in IDE tooltips.
The ServerOnly<T> brand is visible in IDE tooltips and communicates a variable’s origin at a glance, but it does not produce a TypeScript compile error on its own due to structural typing. To get a hard build-time error when env.ts is imported in a client bundle, add import 'server-only' at the top of src/env.ts. Next.js will then throw at build time if the module is included in any client bundle.

NEXT_PUBLIC_ prefix enforcement

The nextjs adapter checks every key in your client schema before any validation runs. If a key is missing the NEXT_PUBLIC_ prefix, createEnv throws immediately:
This is a hard fail-fast. Without the prefix, Next.js silently omits the variable from the browser bundle and you get undefined at runtime with no warning. next-safe-env surfaces the mistake at startup instead.

Browser context stripping

When your code runs in a browser context (typeof window !== 'undefined'), the nextjs adapter strips every non-NEXT_PUBLIC_ key from the returned env object before it reaches your component. This is a runtime safety net layered on top of the TypeScript enforcement - even if a server var somehow ends up referenced in a client bundle, its value will be undefined rather than the secret it holds. On the server side, all vars from both schemas are returned unchanged.

Adapter auto-detection

You do not need to set adapter: 'nextjs' explicitly in most cases. next-safe-env auto-detects the correct adapter using this order: Next.js App Router sets NEXT_RUNTIME automatically, so detection is reliable. Set adapter: 'nextjs' explicitly if you want to suppress the Pages Router heuristic warning or prefer predictable, self-documenting config.

What happens when validation fails

If any variable is missing or invalid, the app refuses to start and prints every problem at once. You never see just the first failure.
Fix all reported values in your .env file or deployment environment and restart. The full list is always shown before exit.