import vs require – ESM & commonJs module differences

ECMAScript Modules (ESM) are still in stage 3 (not finalized).

require import
Dynamic evaluation Static evaluation
Throws error at runtime Throws error while parsing
Non lexical Lexical

As Node.js uses commonJS moduling, I will refer to the workflow of node to address the commonJS modules. And will refer the ECMAScript modules as ESM.

Syntax difference







Loading technique difference

What Node.js does with modules is, it wraps all the code inside a function scope. So the dep.js will look like below.

So be it __filename, __dirname or module, they are not actually global but local to that particular module.

The actual loading of any module using require() happens in 5 steps.

  • Resolution
  • Loading
  • Wrapping
  • Evaluation
  • Caching

The first step resolution is an internal step where node.js calculates the file paths etc. In the second one, which is loading, node pulls the code in the ongoing process. In wrapping in wraps up the code in the function as shown above and then sends it to VM for evaluating and then eventually caches it.

So, basically node never knows what symbols a commonJS module is going to export until and unless the module is actually evaluated. And this is the biggest difference with ECMAScript modules, because ESM is lexical and thus, the exported symbols are known before the code is actually evaluated.

When an ESM module is parsed, then before it is evaluated by the VM, an internal structure called a Module Record is created. This module record keeps the list of exported symbols at the time of parsing. Thus, when you use import {f} from "foo", it actually creates a link between this f and the f which is there in module record’s symbols list (in case f is actually exported).
As a result, any error regarding the unavailability of mismatch of any exported symbol will raise an error before the evaluation.


There is a proposal of import() function too to create nested import statements. Unlike the lexical import keyword, import() function is processed at the time or evaluation (more like require). The syntax is like the following.

use cases

  • On demand module load is possible.
  • Conditional load of modules are also possible
  • Promise like asynchronous handling.


Which has performance benefits?

As import is still in stage 3 and not implemented by browsers natively, we’re unable to run any performance test. Currently when you use import in your code, your transpilers transpile it back to require, the commonJS moduling system. So for the time being both are same.

Which one is better to use

Though there are no performance benefit at the moment, but I will still suggest to use import over require cause it’s going to be native in JS and may (just because it’s native) perform better than require.

About This Author

Hello! I am Paul Shan, a JavaScript Expert, Full Stack and DevOps Engineer cum Consultant based out of Bengaluru, India.

  • 이상철