A named import from an ES module (ESM) can stop at either of these errors.
SyntaxError: The requested module './x.js' does not provide an export named 'foo'
SyntaxError: Named export 'foo' not found. The requested module './x.cjs' is a CommonJS module, which may not support all module.exports as named exports.
The first says “there is no export by that name”; the second says “the other side is CommonJS, and it cannot treat that name as a named export”. Unless the name is a typo, the cause is usually that you are named-importing a CommonJS module from ESM.
Why a named import from CommonJS can fail
A CommonJS module exports one object, module.exports. That is a different shape from ESM’s named exports. Even so, named import works most of the time, because Node analyses the CommonJS source statically and picks up assignments it can follow directly, such as module.exports.foo = ..., as named exports (the Node 20 / 22 documentation describes this analysis as cjs-module-lexer).
The problem is the ways of writing it that the analysis cannot pick up. Computing the key before assigning, for example, cannot be analysed.
// analysable (import { foo } usually works)
module.exports.foo = () => {};
// not analysable (import { foo } is "not found")
const name = 'foo';
module.exports[name] = () => {};
That is why one package can be named-imported while another CommonJS package fails. What decides it is not how you wrote your import; it is how the other package wrote its exports.
Reproduction (minimal)
Write a CommonJS module that exports in a form the static analysis cannot pick up.
// cjs-dep.cjs (.cjs is always CommonJS)
const name = 'greet';
module.exports[name] = function () {
return 'hi from CJS';
};
Named-import it from ESM.
// index.js (package.json has "type": "module", so this is ESM)
import { greet } from './cjs-dep.cjs';
console.log(greet());
Running it prints Named export 'greet' not found ... and exits with code 1.
The fix: take the default import, then pull the name out of it
Receive the whole of module.exports through a default import and take what you need from it. The error message itself suggests this shape.
// index.js (default import, then destructure)
import pkg from './cjs-dep.cjs';
const { greet } = pkg;
console.log(greet());
This runs and exits with code 0. pkg is module.exports itself, so the value is read out of module.exports at run time rather than through the static analysis. If what you take out is undefined, check that the property really exists on the CommonJS side (and that the name is not a typo).
How it works: a named import is resolved before the module runs
ESM’s named import checks whether the name really exists before any code runs. When the other side is ESM, its export statements answer that. When the other side is CommonJS, there are no export statements, so Node infers the named exports by static analysis. A name it could not infer counts as absent, and the failure is a SyntaxError raised before execution.
A default import does no per-name check. It receives one module.exports object, so it can read a property however that property came to exist at run time.
If it still doesn’t work
- It happens against your own ESM file: for
does not provide an export named 'foo', check that the ESM really has anexport, and that the name matches (case, and default vs named).export defaultpairs withimport foo from ...; a named export pairs withimport { foo } from .... - The default-imported
pkgturned out to be the function itself: with CommonJS written asmodule.exports = function () {},pkgis the function. Callpkg()rather thanpkg.foo. - An ESM file said
requireis not defined: that is a different symptom. See require is not defined in ES module scope. require()ing CommonJS produced a different error: if you were not reading CJS from ESM butrequire()ing an ES module from CJS, see require() of ES Module not supported (ERR_REQUIRE_ESM).