/config/nodejs/package-json-exports
package.json exports field: entry points, subpaths and conditions
verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash) · v24.20.0updated
- tool
- Node.js
- file
- package.json
- key
- exports
- type
- string | object (subpaths and/or conditions) | array
- default
- none (main is used, and every file of the package can be imported)
The exports field in package.json is the public interface of a package: it maps import specifiers ("pkg", "pkg/utils") to files, and can pick a different file for import and require. Once exports is present, any path that is not listed is blocked with ERR_PACKAGE_PATH_NOT_EXPORTED, and it takes precedence over main.
Example
{
"name": "mylib",
"exports": {
".": {
"import": "./index.mjs",
"require": "./index.cjs"
},
"./utils": "./lib/utils.js",
"./package.json": "./package.json"
}
}See it resolve
mkdir -p node_modules/mylib/lib node_modules/mylib/internal
cd node_modules/mylib
cat > package.json <<'EOF'
{ "name": "mylib", "exports": { ".": { "import": "./index.mjs", "require": "./index.cjs" }, "./utils": "./lib/utils.js" } }
EOF
echo 'export default "ESM build";' > index.mjs
echo 'module.exports = "CommonJS build";' > index.cjs
echo 'module.exports = "utils";' > lib/utils.js
echo 'module.exports = "secret";' > internal/secret.js
cd ../..
node -e 'console.log(require("mylib"), "/", require("mylib/utils"))'
node --input-type=module -e 'import m from "mylib"; console.log(m)'
node -e 'try { require("mylib/internal/secret.js") } catch (e) { console.log(e.code + ": " + e.message) }'Output
CommonJS build / utils ESM build ERR_PACKAGE_PATH_NOT_EXPORTED: Package subpath './internal/secret.js' is not defined by "exports" in /tmp/work/node_modules/mylib/package.json
require got the .cjs file and import the .mjs file. The internal file exists on disk but cannot be imported, because it is not listed.
Conditions
| Condition | Matches when |
|---|---|
| import | The package is loaded with import or import() |
| require | The package is loaded with require() |
| node | Any Node.js environment |
| module-sync | Node.js versions that can require() ES modules (no top-level await) |
| default | Always; must come last as the fallback |
| types | TypeScript: the .d.ts file for that entry (put it first) |
| browser, development, production | Community conditions used by bundlers |
Node.js picks the first matching key in object order, so the order of the conditions matters: specific ones first, default last.
Patterns
{
"exports": {
".": "./index.js",
"./features/*": "./src/features/*.js",
"./internal/*": null
}
}* maps a whole folder of subpaths, and null explicitly blocks a pattern.
Sources
Related pages
config · nodejspackage.json "type": "module": ES modules in Node.jsSetting "type": "module" in package.json makes Node.js load .js files as ES modules: import and export work, require is undefined, and .cjs stays CommonJS.compare · nodejsrequire vs import in Node.js: CommonJS vs ES modulesrequire() loads CommonJS synchronously at runtime; import is static ES module syntax with top-level await. How Node.js picks one, and how to mix them.errors · typescriptTS2307: Cannot find module 'x' or its corresponding type declarationstsc reports TS2307 when an import resolves to no file and no type declaration. Install the package and its @types, fix the path, or declare the module.