get-next-env
v0.1.0
Published
Type-safe runtime environment variables for Next.js. Build once, deploy everywhere.
Maintainers
Readme
get-next-env
Type-safe runtime environment variables for Next.js. Build once, deploy everywhere.
The Problem
Next.js inlines environment variables (like NEXT_PUBLIC_*) at build time into client JavaScript bundles. This breaks the standard "build once, deploy many" pattern required for containerized applications in Docker or Kubernetes, forcing you to rebuild the container for every environment (dev, staging, prod). Existing workarounds often risk leaking server secrets during SSR or break React 19 / CSP nonces.
The Solution
get-next-env injects filtered environment variables at request time directly into page HTML. One Docker image artifact can be built once and deployed across all environments safely and seamlessly.
import { createEnv } from 'get-next-env';
export const env = createEnv({
GOOGLEMAP_API_KEY: 'GOOGLEMAP_API_KEY',
ENVIRONMENT: { env: 'NEXT_PUBLIC_ENVIRONMENT', default: 'local' },
});Installation
npm install get-next-env
# or
pnpm add get-next-env
# or
yarn add get-next-envUsage
1. App Router (app/layout.tsx)
Inside <head>:
import { EnvScript } from 'get-next-env';
import { env } from '../env.config';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<EnvScript env={env} />
</head>
<body>{children}</body>
</html>
);
}2. Pages Router (pages/_document.tsx)
Inside <Head>:
import { Head, Html, Main, NextScript } from 'next/document';
import { EnvScript } from 'get-next-env';
import { env } from '../env.config';
export default function Document() {
return (
<Html>
<Head>
<EnvScript env={env} />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}3. Usage Modes
Mode 1: Without Validator (Zero Dependencies)
import { createEnv } from 'get-next-env';
export const env = createEnv({
GOOGLEMAP_API_KEY: 'GOOGLEMAP_API_KEY',
ENVIRONMENT: { env: 'NEXT_PUBLIC_ENVIRONMENT', default: 'local' },
});Mode 2: With Zod (Standard Schema)
import { createEnv } from 'get-next-env';
import { z } from 'zod';
export const env = createEnv({
GOOGLEMAP_API_KEY: { env: 'GOOGLEMAP_API_KEY', schema: z.string().startsWith('AIza') },
ENVIRONMENT: { env: 'NEXT_PUBLIC_ENVIRONMENT', schema: z.enum(['local', 'dev', 'staging', 'prod']), default: 'local' },
});Mode 3: With Valibot or Standard Schema Compliant Validator
import { createEnv } from 'get-next-env';
import * as v from 'valibot';
export const env = createEnv({
GOOGLEMAP_API_KEY: { env: 'GOOGLEMAP_API_KEY', schema: v.pipe(v.string(), v.startsWith('AIza')) },
});Accessing Variables Anywhere
import { env } from '../env.config';
const apiKey = env.get('GOOGLEMAP_API_KEY');Startup Validation (Optional)
Call validate() during application initialization or server startup to ensure required variables are present:
env.validate();
// Throws: [get-next-env] Missing "GOOGLEMAP_API_KEY" (process.env.GOOGLEMAP_API_KEY)API Reference
createEnv(config)
Factory function to define allowed environment variables.
- Returns: Object with
.get(key),.validate(), and.__serialize().
<EnvScript env={env} nonce={nonce} />
React component that injects window.__NEXTENV into the page HTML via a plain <script> element.
- Props:
env: Instance returned bycreateEnv.nonce(optional): Content Security Policy (CSP) nonce string.
env.validate()
Validates that required environment variables exist and conform to their schemas. Supports three validator interfaces (checked in order):
- Standard Schema (
~standardproperty) — Zod ≥3.24, Valibot, ArkType safeParse()— Zod (all versions)parse()— Any validator with a parse method
If no schema is provided, validate() only checks that required variables are present.
Security Model
- Strict Allowlist Filtering:
.get()never reads rawprocess.envdynamically; it only accesses the pre-built allowlist cache. - SSR Secret Leak Prevention: During SSR, non-allowlisted server secrets in
process.envare never accessible or serialized. - XSS Protection:
safeSerializeencodes HTML breakout sequences (<,>,&) and Unicode line separators (\u2028,\u2029). - Prototype Pollution Protection: Internal cache is instantiated using
Object.create(null). - CSP Nonce Support: Compatible with custom CSP nonces and includes
suppressHydrationWarning.
Important: Only include variables in your
createEnvconfig that you are comfortable exposing to the browser.
Why Not Alternatives?
| Feature | get-next-env | next-runtime-env | @t3-oss/env-nextjs | next-public-env | | --- | --- | --- | --- | --- | | Runtime injection | ✅ | ✅ | ❌ (build-time) | ✅ | | Pages Router | ✅ | ❌ (dropped) | ✅ | ❌ | | App Router | ✅ | ✅ | ✅ | ✅ | | Standalone output | ✅ | ❌ | N/A | Unknown | | SSR leak prevention | ✅ | ❌ | N/A | ❌ | | CSP nonce (no hydration error) | ✅ | ❌ | N/A | ❌ | | Next.js 16 / React 19 | ✅ | ❌ | ✅ | ❌ | | Validator-agnostic | ✅ (Standard Schema) | N/A | ❌ (Zod only→Standard Schema) | ❌ (Zod required) | | Works without validator | ✅ | ✅ | ❌ | ❌ | | Zero dependencies | ✅ | ✅ | ❌ | ❌ (Zod) | | Client bundle size | <300 bytes | ~5KB | ~2KB | ~275 bytes |
Edge Runtime
Edge Runtime support has not been fully verified. Please open an issue on GitHub if you need Edge Runtime support or encounter issues.
Contributing
Contributions are welcome! Please check out CONTRIBUTING.md to get started.
License
MIT © Manish Thomas
