Skip to content

Conditional Exports (node/browser) + Correct Types

How to publish different entry points for Node vs browser (or edge) using package.json exports conditions, without breaking TypeScript consumers.

Rajesh DhimanMarch 01, 20263 min read
On this page

What you’ll learn

  • What conditional exports are and why they exist
  • How bundlers/Node pick different files via conditions
  • How to keep .d.ts aligned with each condition
  • Common failure modes and how to avoid them

30‑second interview answer

Conditional exports let a package provide different entry points depending on the environment (e.g. node vs browser). Modern tooling reads package.json.exports and chooses the best match based on conditions. In TypeScript, you must ensure the types you expose match the JS entry point chosen by the resolver; otherwise consumers get runtime behavior that doesn’t match compile-time types.


Mental model: One import, multiple possible files

A consumer writes:

import { hash } from "@acme/crypto";

But the resolver might load different files:

  • Browser build → dist/browser.js
  • Node build → dist/node.js

Same import, different runtime.


Example: conditional exports map

{
  "name": "@acme/crypto",
  "type": "module",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "browser": "./dist/browser.js",
      "node": "./dist/node.js",
      "default": "./dist/node.js"
    }
  }
}

Notes

  • default is the fallback when no condition matches.
  • Order matters: tooling picks the best condition it supports.

The tricky part: types must match reality

If browser.js does not support the same API as node.js, you have options:

Option A (best DX): keep the API identical

Both builds export the same functions with the same semantics as much as possible.

Option B: make environment differences explicit in types

Expose a narrower API in the browser build, but then you must provide different .d.ts per condition (advanced, easy to get wrong).

A safer pattern: export a function that reveals capability at runtime:

export function supportsNativeCrypto(): boolean;

Common failure modes

1) Types point to Node implementation but browser loads browser file

  • TS compiles against Node signatures
  • Browser runtime uses different implementation
  • Result: runtime errors or subtle behavior mismatch

2) “It works in webpack, breaks in Node”

  • Bundler supports browser condition
  • Node picks node/default

Fix: test both resolution paths (or use NodeNext in CI for Node consumers).


Production rule of thumb

  • Use conditional exports only when you truly need it.
  • Prefer one stable API across environments.
  • Keep types aligned with the chosen runtime entry.

Interview questions

  1. Q: Why do conditional exports exist?

    • A: To let packages ship optimized builds per environment without changing import paths.
  2. Q: What’s the TypeScript risk?

    • A: The type surface can diverge from the actual runtime entry point loaded.

Quick recap

  • exports can choose different files via conditions.
  • Types must match the runtime entry chosen.
  • Prefer identical APIs across conditions for sane maintenance.
Companion Book

Looking for structured, offline interview prep?

Free online lessons • Portable Amazon Kindle / Paperback

Frontend Interview Guide (2025 Edition) cover
Recommended for React & Frontend Rounds

Frontend Interview Guide (2025 Edition)

By Rajesh Dhiman

Comprehensive preparation covering React internals, Web Performance, CSS architecture, and system design questions.

View on Amazon ↗Kindle & Paperback
JavaScript Interview Guide cover
Recommended for Core JS Rounds

JavaScript Interview Guide

By Rajesh Dhiman

Deep dive into the Event Loop, microtask draining, closures, prototypal inheritance, and asynchronous execution patterns.

View on Amazon ↗Kindle & Paperback

Note: All lessons on Frontend Interview remain completely free to read online. As an Amazon Associate, we may earn from qualifying purchases.

RD

Written by Rajesh Dhiman

Authored by Rajesh Dhiman and engineered at Eunix Tech. Focused on deep-dive frontend engineering, JavaScript runtime internals, React architecture, and web performance.