advanced
Package exports
Design package public APIs with exports maps, conditional exports, subpath exports, types, and backwards compatibility.
`package.json` field `exports` defines the public entry surface — only listed paths are importable, which prevents accidental deep imports and enables conditional targets.
{
"name": "my-lib",
"type": "module",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
},
"./utils": "./dist/utils.js"
}
}
Conditions include `import`, `require`, `node`, `default`, and custom names tools may pass. Subpath patterns (`./features/*`) scale public APIs without exporting the whole tree.
Publishing both ESM and CJS requires distinct files or careful dual build — `exports` documents which file each consumer gets.
On interviews: explain how `exports` blocks `pkg/src/internal`; design backwards-compatible subpath additions; relate `types` condition to TS 4.7+ resolution.
Common pitfalls: forgetting `types` in exports; breaking consumers by removing undocumented paths that were relied upon; mismatched `import`/`require` targets.
The trade-off is balancing simplicity, performance, safety, and operability — name which axis you optimized and what cost you accepted.
Checklist:
- Explicit `exports` for libraries.
- Add subpaths instead of deep src imports.
- Test both ESM and CJS consumers if dual.
- Document public API surface in changelog.