dotenv-webpack

A secure Webpack plugin that loads dotenv variables into your bundle while exposing only what your code actually references.

Library
npm
v9.0.0
1,295stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
48/100Fair
Development Activity4
Maintenance32
Community56
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
67/100Good
Architecture68
Code Quality78
Innovation50
Learning Curve70

dotenv-webpack wraps dotenv-defaults and Webpack’s DefinePlugin to bring environment variables from a .env file into your client bundle. Instead of injecting every variable in the file, it scans for process.env.[NAME] references in your source and only bakes in the ones your code explicitly uses — so secrets like database passwords or API keys that live in .env but are never referenced never end up in shipped JavaScript.

On top of the core load-and-replace behavior, it layers in .env.example “safe mode” validation (fail the build if a required variable is missing), .env.defaults fallback values, system environment variable merging for CI, and variable expansion/interpolation for values that reference other variables. Since Webpack 5 dropped its automatic Node.js polyfills, the plugin also stubs any remaining unreplaced process.env references so browser bundles don’t throw at runtime.

What You Get

  • Compile-time replacement of process.env.NAME references using Webpack’s DefinePlugin, so only referenced variables reach the bundle
  • “Safe mode” via an .env.example blueprint file that throws a build error when a required variable is missing or empty
  • .env.defaults fallback support (via dotenv-defaults) for shared default values across environments
  • Optional systemvars merging so CI/CD environment variables are picked up alongside the .env file
  • Variable expansion/interpolation (${OTHER_VAR} syntax) for composing values from other variables
  • Automatic process.env stubbing under Webpack 5+ targets that lack a Node.js process polyfill, avoiding runtime crashes in browser bundles
  • Configurable path, prefix (e.g. swap process.env. for import.meta.env.), and silent-mode logging

Common Use Cases

  • Injecting API base URLs, feature flags, or public keys into a frontend bundle without exposing unrelated secrets from a shared .env file
  • Enforcing that all required environment variables are set before a CI build succeeds, using safe mode against .env.example
  • Sharing one .env file across multiple packages in a monorepo, each with its own Webpack config pointing at a relative path
  • Migrating a Node-only codebase that used plain dotenv into a bundled frontend build without redoing environment variable handling

Under The Hood

Architecture The entire plugin is a single Dotenv class in src/index.js implementing Webpack’s standard plugin interface via apply(compiler). Internally it follows a clear pipeline: gatherVariables() merges system environment variables, the parsed .env file, and optional .env.defaults values (validating against an .env.example blueprint when safe mode is enabled), then formatData() shapes the result into the key/value map Webpack’s DefinePlugin expects, applying variable interpolation and a Webpack-5-specific process.env stub before handing the assembled DefinePlugin instance to the compiler. There’s no build step or bundler needed to consume the plugin itself, and state is scoped entirely to the options passed into the constructor, so multiple instances in a monorepo don’t interfere with each other.

Tech Stack Plain CommonJS JavaScript with a single runtime dependency, dotenv-defaults (itself a wrapper around dotenv adding defaults-file support), and a peer dependency on webpack ^4 || ^5. Type consumers get a hand-written types/index.d.ts using export = CJS-style declarations rather than a TypeScript build pipeline. Development tooling includes Jest 30 for testing, standard for zero-config linting enforced through a Husky precommit hook, and jsdoc for generating API docs from inline comments.

Code Quality Tests in test/index.test.js run real Webpack compilations against a matrix of fixture .env files (empty, simple, one-empty, missing-one, systemvars, expanded, defaults) and assert on the literal replaced content of the emitted bundle rather than mocking Webpack internals, giving high confidence the plugin behaves correctly end-to-end. Coverage collection is enabled by default in the Jest config, and standard linting is enforced pre-commit. Error handling follows a deliberate two-tier pattern: silent-by-default warnings for missing .env files, but hard throws when safe mode is enabled and a required variable is absent — an explicit, documented choice rather than a swallowed failure.

API Design The entire public surface is a single constructor option object with well-chosen defaults (path: './.env', prefix: 'process.env.'), so the plugin works with zero configuration in the common case. Every option is documented with JSDoc directly above the constructor, and advanced behaviors — safe mode, defaults files, system variable merging, expansion, a custom prefix for import.meta.env.-style access — are additive flags rather than separate APIs, keeping the boilerplate needed to get started minimal.

Used by 5 apps in this directory

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search