npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

next-dynamic-env

v1.2.2

Published

Runtime environment variables for dockerized Next.js apps

Downloads

47

Readme

Next Dynamic Env

npm version npm downloads license

Type-safe runtime environment variables for Next.js – no more rebuilding for config changes!

Why?

Traditional Next.js apps bake environment variables into the build. This means you need to rebuild your entire application just to change an API URL or feature flag. With next-dynamic-env, your containerized Next.js apps can read environment variables at runtime, just like traditional server applications.

Perfect for:

  • 🐳 Docker deployments where the same image runs in multiple environments
  • ☸️ Kubernetes configurations with ConfigMaps
  • 🚀 CI/CD pipelines that promote the same build through stages
  • 🔧 Feature flags and config that change without code changes

Key benefit: Build your Docker image once without environment variables, then inject them at runtime in each environment!

Features

  • Runtime Configuration - Change environment variables without rebuilding
  • Type Safety - Full TypeScript support with autocompletion
  • Security First - Server secrets never reach the browser
  • Any Validator - Works with Zod, Yup, Valibot, or any standard-schema validator
  • Universal - Works everywhere: App Router, Pages Router, middleware, and instrumentation

Installation

npm install next-dynamic-env
yarn add next-dynamic-env
pnpm add next-dynamic-env
bun add next-dynamic-env

Quick Start

1. Define your environment variables

// env.ts
import { createDynamicEnv } from 'next-dynamic-env';
import { z } from 'zod';
import * as yup from 'yup';

export const { clientEnv, serverEnv } = createDynamicEnv({
  client: {
    // Validate with Zod ..
    API_URL: [process.env.API_URL, z.string().url()],

    // .. or with Yup ..
    PORT: [process.env.PORT, yup.number().positive().default(3000)],

    // .. or with any `standard-schema` library ...
    // https://github.com/standard-schema/standard-schema

    // .. or just provide a raw value (no validation)
    PUBLIC_KEY: process.env.PUBLIC_KEY,
  },
  server: {
    // Never exposed to browser
    DATABASE_URL: [process.env.DATABASE_URL, z.string().url()],
    SECRET_KEY: [process.env.SECRET_KEY, z.string().min(32)],
  }
});

2. Add the script to your app

// app/layout.tsx (App Router)
// pages/_app.tsx (Pages Router)
import { DynamicEnvScript } from 'next-dynamic-env';
import { clientEnv } from './env';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <DynamicEnvScript clientEnv={clientEnv} />
      </body>
    </html>
  );
}

3. Use anywhere

import { clientEnv, serverEnv } from './env';

// Server components can access both
export default async function Page() {
  const data = await getSomeData(
    clientEnv.API_URL,
    serverEnv.DATABASE_URL
  );

  return <ClientComponent />;
}

// Client components only access clientEnv
function ClientComponent() {
  return <div>API: {clientEnv.API_URL}</div>;
}

Validation

Use any validator that supports standard-schema:

// With Zod
const { clientEnv } = createDynamicEnv({
  client: {
    PORT: [process.env.PORT, z.coerce.number().default(3000)],
    FEATURES: [process.env.FEATURES, z.string().transform(s => s.split(','))],
  }
});

// With Yup
import { number } from 'yup';

const { clientEnv } = createDynamicEnv({
  client: {
    PORT: [process.env.PORT, number().positive().default(3000)],
  }
});

// Mix validators or use none
const { clientEnv } = createDynamicEnv({
  client: {
    VALIDATED: [process.env.VALIDATED, z.string()],
    RAW_VALUE: process.env.RAW_VALUE, // No validation
  }
});

Advanced Features

waitForEnv (Client-side initialization)

For code that runs before the script tag executes (like instrumentation-client.ts):

import { waitForEnv } from 'next-dynamic-env';

await waitForEnv();
// Now environment variables are available

Empty String Handling

Empty strings are converted to undefined by default (configurable):

// Environment: OPTIONAL_URL=""
createDynamicEnv({
  client: {
    OPTIONAL_URL: [process.env.OPTIONAL_URL, z.string().url().optional()],
  },
  // emptyStringAsUndefined: true (default)
});
// Result: clientEnv.OPTIONAL_URL === undefined ✅

Error Handling

createDynamicEnv({
  // ...your config
  onValidationError: 'throw', // Default: throws on invalid env vars
  // or 'warn' to console.warn
  // or custom function: (error) => { /* handle */ }
});

Docker & Kubernetes

Perfect for containerized deployments where environment changes between stages:

# Same image, different environments
docker run -e API_URL=https://staging.api myapp:latest
docker run -e API_URL=https://prod.api myapp:latest

Build Phase Behavior

During next build, validation is automatically skipped to support Docker workflows where environment variables are injected at runtime rather than build time. This means:

  • ✅ Your Docker image builds successfully without environment variables
  • ✅ Validation still runs at runtime when the container starts
  • ✅ Schema transformations (defaults, type coercion) are still applied during build

This enables true "build once, deploy anywhere" workflows:

# Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm ci
# No ENV vars needed during build!
RUN npm run build

# Runtime - inject environment variables
CMD ["npm", "start"]
# Deploy the same image to different environments
docker run -e DATABASE_URL=$STAGING_DB myapp:latest  # Staging
docker run -e DATABASE_URL=$PROD_DB myapp:latest     # Production

Examples

API Reference

createDynamicEnv(config)

Creates your environment configuration.

  • client: Variables exposed to the browser
  • server: Server-only variables (never sent to browser)
  • onValidationError: 'throw' | 'warn' | (error) => void
  • emptyStringAsUndefined: Convert "" to undefined (default: true)
  • skipValidation: Skip validation (default: false, automatically true during build)

DynamicEnvScript

React component that injects client variables.

  • clientEnv: Your client environment object (required)

waitForEnv(options?)

Waits for environment variables to be available.

  • timeout: Max wait time in ms (default: 5000)
  • requiredKeys: Keys that must be present

Security

Server variables are never exposed to the browser:

  • In development: Accessing server vars on client throws an error
  • In production: Server vars return undefined on client

Contributing

Contributions are welcome! Please see our Contributing Guide for details.

Acknowledgments

This library was inspired by t3-env, which pioneered the concept of type-safe environment variables with server/client separation in Next.js applications. I've built upon their excellent foundation to add runtime configuration capabilities for containerized deployments.

License

MIT