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.
next-safe-env runs in four different execution environments - Next.js App Router, plain Node.js, Vercel Edge Runtime, and Vite - and each one has different rules about which environment variables are accessible. Adapters encapsulate those rules in two hooks: beforeValidate, which runs before any field is checked and can throw immediately on schema violations, and afterValidate, which runs after all fields pass and can strip keys from the result.
Auto-detection
When you omit theadapter option, next-safe-env selects an adapter automatically by inspecting the runtime environment in the following order:
| Condition | Adapter selected |
|---|---|
typeof process === 'undefined' or !process.version | edge |
process.env.NEXT_RUNTIME is set | nextjs |
Any client schema key starts with VITE_ | vite (with a console warning) |
| Otherwise | node |
NEXT_RUNTIME variable is set automatically by the Next.js App Router, so you typically get the right adapter without any configuration. The VITE_ heuristic emits a warning prompting you to set adapter: 'vite' explicitly to suppress it.
The node adapter
The node adapter is designed for Express, Fastify, CLI scripts, and any plain Node.js application that has no client bundle to protect against. Both hooks - beforeValidate and afterValidate - are no-ops. Raw values go in, validated values come out unchanged. All vars from both the server and client schemas are present in the returned object.
client schema is typically empty. You can still use it to group vars that you might share with a frontend, but no prefix enforcement is applied.
The nextjs adapter
The nextjs adapter targets Next.js App Router and Pages Router applications, where the same module graph is compiled for both the server and the browser.
beforeValidate iterates every key in the client schema and throws synchronously if any key does not start with NEXT_PUBLIC_:
NEXT_PUBLIC_ prefix from the browser bundle, so a missing prefix would give you an undefined value in production with no error message. The adapter makes this mistake impossible.
afterValidate checks typeof window !== 'undefined'. When true (browser context), every key that does not start with NEXT_PUBLIC_ is stripped from the result before the object is returned and frozen. On the server, all keys are returned unchanged.
Runtime stripping in
afterValidate is a defense-in-depth measure on top of the TypeScript enforcement. The TypeScript ServerOnly<T> brand and import 'server-only' are your primary tools. See Server/Client Split for the full picture.The edge adapter
The Edge Runtime - used for Vercel Edge Runtime and Next.js Middleware (middleware.ts) - is neither a full Node.js process nor a browser. It only exposes NEXT_PUBLIC_ variables at runtime. Server-only secrets are not available.
beforeValidate does not block validation, but emits a console.warn if the server schema contains any keys:
server schema empty in Edge files.
afterValidate strips every key that does not start with NEXT_PUBLIC_ unconditionally, regardless of typeof window. There is no window in the Edge Runtime, so the browser-context check used by the nextjs adapter would never trigger.
edge and nextjs is when stripping occurs:
| Adapter | When server vars are stripped |
|---|---|
nextjs | Only in browser context (typeof window !== 'undefined') |
edge | Always, unconditionally |
The vite adapter
The vite adapter is for non-Next.js React applications that use import.meta.env. Vite only exposes variables prefixed with VITE_ to the client bundle, so the adapter enforces that prefix on every client schema key - the same way the nextjs adapter enforces NEXT_PUBLIC_.
beforeValidate throws if any client schema key does not start with VITE_.
afterValidate strips server vars from the returned object when running in a browser context.
In Vite projects, use
import.meta.env.VITE_* for client vars in runtimeEnv, not process.env. Server vars that are only available during the build step (such as from a .env file loaded by Vite’s server process) can still use process.env.vite adapter is auto-detected when any client schema key starts with VITE_, but it emits a console warning asking you to set adapter: 'vite' explicitly. Set it explicitly to suppress the warning and make your configuration self-documenting.
When to set adapter explicitly
Auto-detection covers the common cases, but there are situations where you should always set adapter explicitly:
- Vite projects - auto-detection emits a warning; always set
adapter: 'vite'. - Next.js Pages Router -
NEXT_RUNTIMEmay not be set during the initial page render in older Next.js versions; setadapter: 'nextjs'to be safe. - Monorepos with shared env files - if
env.tsis imported from both a Next.js app and a Node.js script, each entry point should callcreateEnv()independently with the correctadapterset explicitly. - Any environment where runtime detection is ambiguous - explicit configuration is always more reliable than heuristics.

