Dual ESM/CJS Package
Publishing & TypeScript
Start Here
Learn how Node.js evaluates ESM and CJS at runtime, why static analysis matters for bundlers, and when each format applies.
Core Problem Navigating the Dual-Package HazardUnderstand why a package can be instantiated twice in one process, how singleton state breaks, and the exports field pattern that prevents it.
Bundle Size Implementing sideEffects CorrectlyConfigure sideEffects in package.json to unlock aggressive dead code elimination across Webpack, Rollup, and esbuild.
Compare the three dominant bundlers for library authors, choose the right one for your project, and emit correct dual ESM/CJS outputs.
Why dual-format publishing matters
JavaScript's module ecosystem is split between CommonJS (CJS) — the legacy synchronous format powering decades of Node.js tooling — and ECMAScript Modules (ESM), the modern static format that enables tree-shaking, top-level await, and native browser support. Library authors must ship both to avoid breaking consumers.
A misconfigured package.json exports field can silently instantiate your
library twice in the same process, corrupting singleton state and breaking framework registries.
Getting this right requires understanding resolution mechanics, TypeScript declaration alignment,
and bundler-specific behaviour.
This site is a practical reference covering every layer of the stack — from Node.js module caches and conditional exports to tsconfig settings, build tooling, and CI validation pipelines.
What you'll learn
- How ESM and CJS execution models differ at runtime
- The exact
exportsfield pattern that prevents dual-package hazards - Tree-shaking: why
sideEffects: falseisn't enough on its own - Barrel file anti-patterns and how they destroy bundle optimisation
- tsconfig options for library authors (
moduleResolution: "bundler", declaration maps) - Build tooling: tsup, esbuild, Rollup for dual ESM/CJS output
- CI/CD matrix testing across ESM and CJS consumer harnesses
- Automated npm publishing with
--provenance, OIDC trusted publishing, and Sigstore attestation
Explore the Guides
ESM vs CJS execution models, Node.js resolution mechanics, the dual-package hazard,
and mastering the package.json exports field.
- Understanding ESM vs CJS
- Navigating the Dual-Package Hazard
- Mastering the exports Field
- Browser vs Node.js Resolution
Dead code elimination, sideEffects flag configuration, barrel file anti-patterns,
and measuring real bundle impact.
- Implementing sideEffects Correctly
- Eliminating Barrel File Anti-Patterns
- Advanced DCE Techniques
- Optimising Bundle Size
tsconfig for library distribution, declaration file generation, path aliases, and modern build tools — tsup, esbuild, and Rollup.
Explore section →
Automated release pipelines with GitHub Actions, Sigstore --provenance attestation,
OIDC trusted publishing, and pre-publish validation with publint and attw.
- npm Provenance & Sigstore Attestation
- Automating npm Releases with GitHub Actions
- Validating Packages Before Publish
- Versioning & Changelog Automation
Featured Guides
-
How to Configure Node.js for Native ESM Support
— Enable
"type": "module", pick the right file extension, and avoid the commonERR_REQUIRE_ESMtrap. -
Fixing
require()Errors in Pure ESM Packages — Step-by-step resolution forERR_REQUIRE_ESMwhen consuming a pure-ESM dependency from CJS code. -
Conditional Exports for Development vs Production
— Use the
exportsmap conditions to serve debug builds in dev and optimised builds in prod. -
Why Barrel Files Break Tree-Shaking in Webpack
— Understand why
index.tsre-exports force Webpack to include every module, and how to fix it. - Configuring sideEffects for CSS and Asset Imports — Correctly whitelist CSS and asset files so bundlers don't eliminate them as dead code.
-
Using tsup to Bundle Dual ESM and CJS Outputs
— Zero-config tsup setup that produces correct
.mjs/.cjsoutputs with matching declaration files. -
Generating Accurate
.d.tsFiles with TypeScript 5.4 — Avoid the common declaration-emit pitfalls introduced byisolatedDeclarationsand the new emit modes. - Migrating from tsc to esbuild for Faster Builds — Drop-in migration guide: keep type-checking with tsc, hand transpilation to esbuild, and cut build times by 10–50×.
-
Publishing with npm Provenance in GitHub Actions
— Wire
id-token: writeandnpm publish --provenanceto emit a verifiable Sigstore attestation on every release. -
Configuring OIDC Trusted Publishing for npm
— Publish from GitHub Actions with short-lived
id-tokencredentials instead of a long-livedNPM_TOKEN. - Rollup vs esbuild vs webpack Bundle Size — A measured head-to-head on the same ESM library, with the tree-shaking trade-offs behind each tool's output.
-
Publishing Packages from a pnpm Workspace
— Resolve
workspace:protocol dependencies, order builds, and ship correct exports for each published package.