How to Use Conditional Exports in package.json

The exports field in package.json allows JavaScript package authors to define explicit entry points and control how consumers access module files based on specific runtime environments, module formats, or conditions. By replacing or augmenting the legacy main field, conditional exports enable modern packages to serve distinct builds—such as ECMAScript Modules (ESM), CommonJS (CJS), TypeScript definitions, or browser-specific bundles—from a single package configuration cleanly and securely.

The Basic Structure of Conditional Exports

In its simplest form, the exports field maps module conditions directly to file paths. Instead of pointing to a single file, you provide an object where the keys represent conditions and the values represent the corresponding file paths.

{
  "name": "my-library",
  "exports": {
    "import": "./dist/index.mjs",
    "require": "./dist/index.cjs",
    "default": "./dist/index.cjs"
  }
}

In this configuration: - An ESM import statement loads ./dist/index.mjs. - A CommonJS require() call loads ./dist/index.cjs. - Any other consuming environment falls back to default.

Common Conditional Export Keys

Node.js and module bundlers recognize a standard set of built-in condition keys:

Conditional Subpath Exports

Conditional exports are not limited to the root entry point. You can expose distinct subpaths (such as my-library/utils) with their own set of conditions:

{
  "name": "my-library",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs"
    },
    "./utils": {
      "types": "./dist/utils.d.ts",
      "browser": "./dist/utils.browser.js",
      "node": "./dist/utils.node.js",
      "default": "./dist/utils.js"
    }
  }
}

This ensures consumers can import specific utilities (import { formatDate } from 'my-library/utils') while receiving the optimized build for their targeted runtime.

Nested Conditions

Conditions can be nested to handle complex runtime matrix requirements, such as differentiating between ESM and CJS within a specific platform:

{
  "exports": {
    "node": {
      "import": "./dist/node.esm.js",
      "require": "./dist/node.cjs.js"
    },
    "default": "./dist/universal.js"
  }
}

Condition Ordering and Evaluation Rules

Order matters significantly within the exports object. Tools evaluate conditional keys from top to bottom and stop at the first matching key:

  1. Place specific keys first: General keys like default must always be placed last.
  2. TypeScript typing placement: The types condition must be listed before import or require so TypeScript can resolve types before the runtime entry files.
  3. Encapsulation: Any file or directory inside your package not explicitly defined in the exports field is private and cannot be imported by consumers, preventing accidental access to internal APIs.