> ## 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: typed env vars for Next.js

> next-safe-env validates env vars at startup with full TypeScript inference. Zero dependencies, under 5 kB. Supports Next.js, Node.js, Edge, and Vite.

`next-safe-env` is a zero-dependency library that validates and types your environment variables at startup. You define a schema once - server vars, client vars, their types, and any constraints - and the library either hands you a fully-typed, frozen object or crashes loudly with every problem listed, before your app serves a single request.

## The problem with `process.env`

In every TypeScript project, `process.env.X` is always `string | undefined`. There is no autocomplete, no type coercion, and nothing that stops you from reaching a missing or malformed variable deep inside a request handler. The standard workaround is hand-written guard code repeated across every file that needs an env var:

```typescript theme={null}
// Without next-safe-env - manual guards scattered across your codebase
const DATABASE_URL = process.env.DATABASE_URL
if (!DATABASE_URL) throw new Error('Missing DATABASE_URL')

const PORT = parseInt(process.env.PORT ?? '3000', 10)
if (isNaN(PORT)) throw new Error('PORT must be a number')
```

This approach fails in compounding ways: missing vars are discovered mid-request instead of at startup, there is no single place to audit what your app needs to run, and nothing prevents you from reading a server-only secret in a client component where it silently becomes `undefined` in the browser.

`next-safe-env` replaces all of that with a single function call:

```typescript theme={null}
// With next-safe-env - one schema, full types, startup crash on any failure
import { createEnv, str, url, port, bool } from 'next-safe-env'

export const env = createEnv({
  server: {
    DATABASE_URL: url(),
    PORT:         port().default(3000),
    NODE_ENV:     str().enum(['development', 'production', 'test']),
  },
  client: {
    NEXT_PUBLIC_APP_NAME:     str().default('My App'),
    NEXT_PUBLIC_ENABLE_DEBUG: bool().default(false),
  },
  runtimeEnv: {
    DATABASE_URL:             process.env.DATABASE_URL,
    PORT:                     process.env.PORT,
    NODE_ENV:                 process.env.NODE_ENV,
    NEXT_PUBLIC_APP_NAME:     process.env.NEXT_PUBLIC_APP_NAME,
    NEXT_PUBLIC_ENABLE_DEBUG: process.env.NEXT_PUBLIC_ENABLE_DEBUG,
  },
})

// env.DATABASE_URL  → string       (not string | undefined)
// env.PORT          → number       (coerced from string, not string)
// env.NODE_ENV      → 'development' | 'production' | 'test'
```

## Key features

<CardGroup cols={2}>
  <Card title="Startup crash, never runtime crash" icon="shield-check">
    Every variable is validated synchronously at module load time. If anything is missing or malformed, the process exits with a readable error listing every problem at once.
  </Card>

  <Card title="Full TypeScript inference" icon="code">
    The return type of `createEnv()` is fully inferred from your schema. No separate type definitions to maintain. Add a field and it appears everywhere automatically.
  </Card>

  <Card title="Server/client split" icon="layer-group">
    Server vars and client vars live in separate schema keys. The `nextjs` adapter enforces the `NEXT_PUBLIC_` prefix and strips server vars from the browser bundle at runtime.
  </Card>

  <Card title="Zero dependencies" icon="box">
    No Zod, no dotenv, no peer deps. All validation logic ships in under 5 kB gzipped. Zod interop is available if your project already uses it - but it is never required.
  </Card>

  <Card title="Automatic type coercion" icon="arrow-right-arrow-left">
    Built-in helpers like `num()` and `bool()` automatically convert string environment variables to their correct types so `"3000"` becomes `3000` and `"true"` becomes `true`, without any manual parsing.
  </Card>

  <Card title="Multi-runtime support" icon="bolt">
    Works in Next.js App Router, plain Node.js, Vercel Edge Runtime, Next.js Middleware, and Vite. The correct adapter is auto-detected from the runtime environment.
  </Card>
</CardGroup>

## How it compares

The table below uses data from the project README to compare `next-safe-env` against the most common alternatives.

| Feature                             | next-safe-env |    t3-env    | envalid | dotenv + Zod |
| ----------------------------------- | :-----------: | :----------: | :-----: | :----------: |
| Zero dependencies                   |       ✓       |       ✗      |    ✗    |       ✗      |
| Next.js App Router support          |       ✓       |    Partial   |    ✗    |       ✗      |
| Server/client TypeScript split      |       ✓       |       ✓      |    ✗    |    Manual    |
| Edge Runtime adapter                |       ✓       |       ✗      |    ✗    |       ✗      |
| Fluent validator API                |       ✓       | Schema-based |  Custom | Schema-based |
| Pretty error output                 |       ✓       |    Partial   |    ✓    |    Manual    |
| Bundle size                         |  **\< 5 kB**  |   \~50 kB+   | \~10 kB |   \~50 kB+   |
| Auto-enforce `NEXT_PUBLIC_` prefix  |       ✓       |    Manual    |    ✗    |       ✗      |
| Zod interop                         |    Optional   |   Required   |    ✗    |   Required   |
| `ClientEnv<T>` server-only branding |       ✓       |    Partial   |    ✗    |    Manual    |
| Vite adapter                        |       ✓       |       ✓      |    ✗    |    Manual    |

If your project already uses Zod and you want schema reuse, `t3-env` integrates tightly with it. `next-safe-env` is the right choice when you want the full feature set - typed vars, server/client splitting, Edge Runtime support, and pretty error output - without adding any runtime dependencies.

## Requirements

* **Node.js** 18 or later
* **TypeScript** 5.x
* No runtime dependencies

`next-safe-env` validates what is already in `process.env`. It does not load `.env` files. Use Next.js's built-in `.env` support or `dotenv` to load your files first, then let `next-safe-env` validate and type the result.

## Next step

<Card title="Quick Start" icon="rocket" href="/quickstart">
  Install next-safe-env and define your first typed environment schema in under 5 minutes.
</Card>
