The correct way is to continue using the old import syntax. The new import syntax is for ES modules only, the old import syntax is for pre-ES6 modules. The two are distinct, intentionally so. import * as foo from 'foo' imports all the properties of the module 'foo', it does not import the default value as foo.

From the designer of the feature:

  • An export default declaration always declares an exported member named default and is always emitted as an assignment to exports.default. In other words, export default consistently has ES module semantics. For compatibility with Babel we could optionally emit an __esModule marker when a module has a default export, but we wouldn't actually use that marker for anything.
  • An export = declaration, which substitutes a different entity to be exported in place of the module itself, is always emitted as an assignment to module.exports. It is an error to have other exports in a module that uses export =. This is the existing TypeScript behavior.
  • A module that uses export = to export another module (be that an internal or external module) can be imported using the new ES6 constructs. In particular, the convenient destructuring imports can be used with such modules. The pattern of using export = to export another module is common in .d.ts files that provide a CommonJS/AMD view of an internal module (e.g. angular.d.ts).
  • A module that uses export = to export a non-module entity in place of the module itself must be imported using the existing import x = require("foo") syntax as is the case today.

2016 update: The TypeScript compiler at some point started allowing import * as foo from 'legacy-module-foo' to get the default import of a legacy module in certain circumstances. This is a violation of the ES6 specification (§15.2.1.16, “The value "*" indicates that the import request is for the target module’s namespace object.”).

When legacy modules you import in this manner are updated to ES6 modules, the “default” imports for those modules will stop working (because * as foo imports are supposed to be importing namespace objects), which may be extremely confusing if you don’t know that doing this is a TypeScript/SystemJS hack. It is also possible that a future TypeScript realignment to the ES specification will cause them to break.

As such, you should probably prefer to continue to use the legacy import syntax described above to load legacy modules to avoid confusing yourself and other developers working on your code about how ES6 namespace imports work, and to avoid confusing breaking changes.

Answer from C Snover on Stack Overflow
Top answer
1 of 6
111

The correct way is to continue using the old import syntax. The new import syntax is for ES modules only, the old import syntax is for pre-ES6 modules. The two are distinct, intentionally so. import * as foo from 'foo' imports all the properties of the module 'foo', it does not import the default value as foo.

From the designer of the feature:

  • An export default declaration always declares an exported member named default and is always emitted as an assignment to exports.default. In other words, export default consistently has ES module semantics. For compatibility with Babel we could optionally emit an __esModule marker when a module has a default export, but we wouldn't actually use that marker for anything.
  • An export = declaration, which substitutes a different entity to be exported in place of the module itself, is always emitted as an assignment to module.exports. It is an error to have other exports in a module that uses export =. This is the existing TypeScript behavior.
  • A module that uses export = to export another module (be that an internal or external module) can be imported using the new ES6 constructs. In particular, the convenient destructuring imports can be used with such modules. The pattern of using export = to export another module is common in .d.ts files that provide a CommonJS/AMD view of an internal module (e.g. angular.d.ts).
  • A module that uses export = to export a non-module entity in place of the module itself must be imported using the existing import x = require("foo") syntax as is the case today.

2016 update: The TypeScript compiler at some point started allowing import * as foo from 'legacy-module-foo' to get the default import of a legacy module in certain circumstances. This is a violation of the ES6 specification (§15.2.1.16, “The value "*" indicates that the import request is for the target module’s namespace object.”).

When legacy modules you import in this manner are updated to ES6 modules, the “default” imports for those modules will stop working (because * as foo imports are supposed to be importing namespace objects), which may be extremely confusing if you don’t know that doing this is a TypeScript/SystemJS hack. It is also possible that a future TypeScript realignment to the ES specification will cause them to break.

As such, you should probably prefer to continue to use the legacy import syntax described above to load legacy modules to avoid confusing yourself and other developers working on your code about how ES6 namespace imports work, and to avoid confusing breaking changes.

2 of 6
19

The corresponding syntax for ES6 module syntax is:

import * as foo from 'foo';

Basically import everything from the foo module into a local variable by the name of foo.

🌐
Reddit
reddit.com › r/jsdev › mix commonjs and es6 modules in same project
r/JSdev on Reddit: Mix CommonJS and ES6 modules in same project
May 8, 2021 -

I am working in NodeJS. I have a great deal of legacy code including several packages that are used in many places. This code is all CommonJS, Node require() module structures.

Node now supports ES6. Since it is a Javascript language feature, I would like to migrate to it.

Today, I started a small project. My small project boilerplate requires() a couple of my favorite utilities and then says 'Hello World'. I edited it to import said utilities. Node told me I needed to add "type":"module" to my package.json and I did.

When I ran it, I was told that "require is not defined", this in reference to one of the utility modules I imported.

I infer that this means that a project is either CommonJS or ES6 and it appears that never the twain shall meet. I am surprised by this because it means that I will never use ES6 in NodeJS because I will never be able to change all of the modules I require(). Some are not even mine, others are used in projects (npm!) that I do not even know about.

Honestly, I have a hard time believing that this is the case. I don't understand how ES6 can ever become a widely used standard because of if ES^ and CommonJS cannot be used together in an application. I realize that Webpack, etc, will preprocess code and revise all the require() statements but not everyone uses that sort of utility.

My questions are:

Is this analysis correct?

Is there some workaround that will let me use both module systems (without a preprocessor)?

Is my impending decision to never, ever use ES6 the right one?

UPDATE: It's a couple of years later and I am a little bit mad at all of you. It turns out there is a completely simple workaround that nobody ever mentioned**. IE...**

const thing=await import('thing'); //remember to add "async" to the containing function definition

Top answer
1 of 5
3
I think your frustration is founded, if not a bit dramatic. Your decision to never ever use ES6 is a short term solution that may remain good enough for long enough, but I see ES6 modules more and more frequently, suggesting a trend in that direction - so you may want to consider taking it a bit more seriously. We ran into the same thing recently, and we still switched our fairly mature ExpressJS API to ES6 modules. Is there a workaround that will let you use both module systems? Yes, changing your package.json to "type": "module" enforces you to use import statements, and won't allow require() statements...but that does not mean CJS modules are off the table. You can import a commonJS module just like you required it before (but you lose the ability to use named imports). So your steps are: Change package.json to "type": "module" Change all require()'s to imports Change any named imports: let {abc, def} = require('./my-cjs-module') becomes: import alphabet from './my-cjs-module'; let {abc,def} = alphabet; You can also, instead of changing your top level package.json to "type": "module" add a almost blank package.json in a sub-directory, and just have it's contents be type: module, if you want to tackle this problem one folder at a time. I hope this helps, I'm typing it between counter-strike matches, so it might be a little disjointed, but I'm happy to answer any other questions about how we accomplished it.
2 of 5
3
You can do import { createRequire } from "module" const require= createRequire(import.meta.url)
Discussions

Is it possible to es6 import a commonjs module?
Bring the best of human thought and AI automation together at your work. Explore Stack Internal ... I have a commonjs module, which was generated by Typescript 3.3.3. Is it possible to use it with an es6 import statement? More on stackoverflow.com
🌐 stackoverflow.com
Importing commonJS modules with es6 imports
I'm trying to convert a Babel project with typescript and I realize that I need to change some of the imports from import module from 'module' to import * as module from 'module'... More on github.com
🌐 github.com
13
September 27, 2016
How to include commonjs module in ES6 module node app?
What are my options (as of 5/2020, Node 12.16.3) for combining CommonJS modules into an ES6 app? ... You can mix and match the two? I don't think you'll have any issues. ... Working with CommonJS modules is pretty straight forward. You can only do default exports from CommonJS modules. import ... More on stackoverflow.com
🌐 stackoverflow.com
CommonJS to ES6 rewrite
Bleh - nobody fucking understands this shit. These comments are littered with completely wrong answers. It’s honestly fine to upgrade apps to es6 at anytime with no problem. The real problems come when you want to share a library. You only get more compatibility when you upgrade a standalone app to ESM, but with a shareable library you lose the ability to share with CJS users. If you stick w CJS for your standalone app, you will not be able to use ESM modules. Also note, next js does not support ESM and is entirely cjs, there is no choice here, it’s just cjs. More on reddit.com
🌐 r/node
46
14
August 24, 2023
Top answer
1 of 4
39

In Node.js, if you want to import an ES module in a CommonJS module you can use dynamic import and the .mjs file extension on the ES modules. For example:

index.js CommonJS

const crypto = require('crypto');  // to show this is a commonJS module

import('./path/to/mod.mjs').then(mod =>
  console.log(mod.msg);    //  "Hello world!"
);

mod.mjs ES module

export const msg = "Hello world!";

Two examples of how import could be used within a CommonJS module to import all or some of the lodash-es package:

import('lodash-es').then(_ => {
  console.log(_.pad(_.toUpper('hello world'), 17, '*'));
});
Promise.all([
  import('lodash-es/pad.js'),
  import('lodash-es/toUpper.js'),
])
.then(([{ default: pad }, { default: toUpper }]) => {
  console.log(pad(toUpper('hello world'), 17, '#'));
});

Or you can just import what you need into a different CommonJS module then export a Promise which you can then import or require.

utils.js

module.exports = Promise.all([
  import('lodash-es/pad.js'),
  import('lodash-es/toUpper.js'),
]);

index.js

require('./utils.js').then(([{ default: pad }, { default: toUpper }]) => {
  console.log(pad(toUpper('hello world'), 17, '*'));
}); 
2 of 4
9

ESModules and CommonJS are mutually exclusive, so you can't "use ES6 modules inside CommonJS".

However, in a way, you can "use CommonJS inside ESModules", if by "CommonJS" you only mean the require() function. You can create an instance of require() function with module.createRequire():

import { createRequire } from 'module';
const require = createRequire(import.meta.url);

// sibling-module.js is a CommonJS module.
const siblingModule = require('./sibling-module');

There's a chapter in NodeJS's documentation on interoperability between the two module systems. It's very helpful, you might want to check it out.

🌐
Adam Coster
adamcoster.com › blog › commonjs-and-esm-importexport-compatibility-examples
CommonJS (cjs) and Modules (esm): Import compatibility - Adam Coster
September 5, 2022 - ESM spec requires that import paths be valid paths, meaning you need the file extension and everything (CommonJS doesn't require that). Node has an option to skip out on that requirement for ES modules, if you want to keep it old-school: node --es-module-specifier-resolution=node your-dope-module.mjs · If you do decide to go all-in on ESM in Node, be ready to do a lot of very annoying troubleshooting!
🌐
TechSparx
techsparx.com › nodejs › esnext › esmodules-from-commonjs.html
Loading an ES6 module in a Node.js CommonJS module
A CommonJS module previously COULD NOT load an ES6 Module, but today it can load an ES6 module using the import() function
🌐
Medium
medium.com › @rahul.jindal57 › es6-imports-vs-commonjs-imports-8e9b66aa04bd
ES6 imports vs CommonJS imports in Javascript
May 20, 2023 - Among the transformative changes ... CommonJS reigned as the dominant module system, still widely utilized, particularly in Node.js. To write optimized code, it is essential to understand the core differences between ES6 and CommonJS imports....
Find elsewhere
🌐
DEV Community
dev.to › lico › how-to-import-and-export-in-commonjs-and-es-modules-43m1
How to import and export in CommonJS and ES Modules - DEV Community
January 13, 2023 - You cannot import a module which uses ES Modules as a module system from CommonJS. If you try it, you will see like this error.
🌐
Sliceofdev
sliceofdev.com › posts › commonjs-and-esm-modules-interoperability-in-nodejs
CommonJS and ESM modules interoperability in NodeJS
February 11, 2023 - This is because ES modules have asynchronous execution. Instead, we can use dynamic import() statements to import ESM in CommonJS.
🌐
LinkedIn
linkedin.com › pulse › importing-es-6-modules-from-commonjs-adam-brandizzi
Importing ES 6 Modules from CommonJS
March 21, 2023 - The good news is that this is no longer true! Using dynamic import, we can load ES6 modules from CommonJS. Let’s look at an example. In this project, the importer.js file tries to use require() to import an ES6 module:
🌐
LogRocket
blog.logrocket.com › home › commonjs vs. es modules in node.js
CommonJS vs. ES modules in Node.js - LogRocket Blog
June 20, 2024 - In other words, we can build a library that supports both import and require, allowing us to solve compatibility issues. ... my-node-library ├── lib/ │ ├── browser-lib.js (iife format) │ ├── module-a.js (commonjs format) │ ├── module-a.mjs (es6 module format) │ └── private/ │ ├── module-b.js │ └── module-b.mjs ├── package.json └── …
🌐
GitHub
github.com › microsoft › TypeScript › issues › 11179
Importing commonJS modules with es6 imports · Issue #11179 · microsoft/TypeScript
September 27, 2016 - I understand that the modules I try to import has no "default" export but Babel deals with it by wrapping the import with _interopRequireDefault.
Author   testerez
🌐
TypeScript
typescriptlang.org › docs › handbook › 2 › modules.html
TypeScript: Documentation - Modules
Having been around since 2012, ... (or ES6 modules). You might know it as the import/export syntax. ES Modules was added to the JavaScript spec in 2015, and by 2020 had broad support in most web browsers and JavaScript runtimes. For focus, the handbook will cover both ES Modules and its popular pre-cursor CommonJS module.exports ...
Top answer
1 of 4
43

Working with CommonJS modules is pretty straight forward. You can only do default exports from CommonJS modules.

import packageMain from 'commonjs-package'; // Works
import { method } from 'commonjs-package'; // Errors

This means that all commonjs exports will live on the packageMain object, and you need to dot in to the packageMain object to pickup what you need.

packageMain.method1()

More info in the official nodejs docs

2 of 4
16

Since Node 13.10, there is another option, the most forward-looking one:

File an issue in the repo of the CommonJS library you'd like to use, persuading the maintainers to publish dual packages (ESM + CommonJS), using conditional exports.

For libraries written in TypeScript, generating dual packages is easy, and doesn't require Babel or rollup or any additional tools. Here's how I did it in local-iso-dt:

Relevant parts of package.json:

{
  "name": "local-iso-dt",
  "version": "3.1.0",
  "description": "...",
  "type": "commonjs",
  "exports": {
    "node": {
      "import": "./index.mjs",
      "require": "./index.js"
    },
    "default": "./index.mjs"
  },
  "main": "index.js",
  "files": [
    "index.ts",
    "index.mjs",
    "index.js"
  ],
  "scripts": {
    "clean": "rm index*.js index.mjs",
    "prepublishOnly:cjs": "tsc index.ts --esModuleInterop --removeComments",
    "prepublishOnly:esm": "tsc index.ts -t ES2015 --types node && mv index.js index.mjs",
    "prepublishOnly": "npm run prepublishOnly:esm; npm run prepublishOnly:cjs"
  },
  "devDependencies": {
    "typescript": "^4.0.2"
  },
}

prepublishOnly:esm renames the output manually because TypeScript can't yet generate .mjs output directly and --outFile doesn't work with ES Modules.

The exports block has the "conditional exports that enable TypeScript code transpiled with ES Modules, to use named imports. TypeScript doesn't directly support .mjs input files.

No tsconfig.json was necessary for this simple module.

🌐
Node.js
nodejs.org › api › esm.html
Modules: ECMAScript modules | Node.js v25.9.0 Documentation
These CommonJS namespace objects ... interop support. When importing a CommonJS module, it can be reliably imported using the ES module default import or its corresponding sugar syntax:...
🌐
Reddit
reddit.com › r/node › commonjs to es6 rewrite
r/node on Reddit: CommonJS to ES6 rewrite
August 24, 2023 -

I want to rewrite a application that uses CommonJS modules to ES6 modules in nodejs. It's a simple web app with simple stack (Express, Knex, Mocha, etc) but I will evolve it to a more complex app with more features and modules.
The thing that makes me worry is the current state of the ecosystem and support for ES6 modules in nodejs. Things like drivers for databases, testing frameworks, support libraries (ex: lodash), etc.
Have we passed the point where we can use ES6 modules in nodejs without any problems? Or should I stick with CommonJS for now?

🌐
w3tutorials
w3tutorials.net › blog › mix-commonjs-and-es6-modules-in-same-project
How to Mix CommonJS and ES6 Modules in the Same Node.js Project: Solving Import/Require Conflicts — w3tutorials.net
3. Avoid Named Imports from CJS in ESM : CJS modules don’t have true named exports, so always import the default export and destructure: import cjsModule from './utils.cjs'; const { foo } = cjsModule; 4. Use Dynamic import() for ESM in CJS ...
🌐
PäksTech
pakstech.com › blog › node-import-error
Import ES6 Modules in Node.js | PäksTech
January 5, 2021 - The opposite is not true, so you cannot require ES6 modules since they are loaded asynchronously. You could use the dynamic import() function which is asynchronous but try to avoid it if at all possible. You can’t await code on the top level of CommonJS code which makes the dynamic import a real hassle to use.
🌐
xjavascript
xjavascript.com › blog › how-to-include-commonjs-module-in-es6-module-node-app
How to Include CommonJS Modules in an ES6 Module Node.js App Without Transpiling (Node 12.16.3+) — xjavascript.com
Handle __dirname in Legacy Code For CommonJS modules needing __dirname, pass it explicitly from ESM using import.meta.url. Node.js 12.16.3+ enables seamless interoperability between ES6 modules and CommonJS without transpilation.
🌐
Fireship
fireship.dev › javascript-modules-iifes-commonjs-esmodules
JavaScript Modules: From IIFEs to CommonJS to ES6 Modules
... There's one more difference ... even conditionally. ... Because ES Modules are static, import statements must always be at the top level of a module....