In this lesson, you will learn how to bridge the gap between CommonJS (CJS) and ECMAScript Modules (ESM) when dealing with default exports.
The Compatibility Challenge
When you use the require syntax (CJS) to import a modern ES Module, or use import (ESM) to bring in older CJS code, they don't always "speak the same language" regarding default values.
In CJS, a module exports a single object via module.exports. In ESM, a module can have a default export alongside several named exports. When Node.js performs the interop, it tries to map these concepts together, but the behavior can be surprising.
Importing CJS in ESM
If you have a CJS module that uses module.exports = { ... }, when you import it into an ESM file, Node.js treats the entire object as the default export.
// math.cjs
module.exports = { add: (a, b) => a + b };
// app.mjs
import math from './math.cjs';
console.log(math.add(1, 2)); // Works as expected!
However, if the CJS module uses exports.add = ... (a named export), you cannot use the default import syntax; you must use the named import: import { add } from './math.cjs'.
The "Default" Trap
The most common friction point occurs when you try to import a CommonJS module that has both module.exports and named properties. Because ESM expects a specific default property, if your CJS file looks like this:
// CJS file
module.exports = { data: 'hello' };
module.exports.other = 'world';
When importing this into ESM, you often need to use the "namespace import" syntax to capture everything safely:
import * as myModule from './legacy.cjs';
// myModule.default.data -> 'hello'
// myModule.other -> 'world'
By using the * as syntax, you create a static object that contains both the default export and any named exports, effectively shielding your application from the underlying module resolution differences. This ensures your code remains predictable regardless of how the source module was originally structured.
Key Takeaway: When mixing module standards, use namespace imports (import * as x) to ensure you capture both default and named exports consistently across the CJS/ESM boundary.