HomeBlogWeb DevelopmentCommonJS vs ES Modules in Node.js [A Detailed Comparison]

CommonJS vs ES Modules in Node.js [A Detailed Comparison]

Published
24th May, 2024
Views
view count loader
Read it in
13 Mins
In this article
    CommonJS vs ES Modules in Node.js [A Detailed Comparison]

    Node.js has transformed JavaScript from a client-side language to a powerhouse in both frontend and backend development. It's the driving force behind major frameworks like React and Vue, providing speed and scalability for modern applications. Within this versatile environment, developers encounter two main module systems: CommonJS (CJS) and ES Modules (ESM). 

    CommonJS, synonymous with Node.js’s origins, offers simplicity and synchronous loading, ideal for server-side applications. On the flip side, ES Modules align with the ECMAScript standard and bring asynchronous loading, increasingly relevant in both browser and server contexts. 

    Understanding these two systems is crucial for Node.js developers. This article will compare CommonJS and ES Modules, guiding you to choose the right approach for your project, considering factors like compatibility and project requirements. So now let's dive into the world of Node.js modules and discover how they can shape your development workflow. Further, explore Node.js best course to increase your understanding of CommonJS vs ES Modules

    CommonJS vs ES Modules in Node.js [Comparison Table]

    Let us have a look at CommonJS vs ES modules in Node.js in depth:

    Functionality 

    CommonJS 

    ES Module 

    Functionality 

    Works with the Node.js platform 

    Works with the web browser environment 

    Compilation 

    Compiled into AMD modules 

    Does not require a module loader like AMD

    Dependencies 

    All dependencies are listed in the same file 

    Reference any other module in the same package available on the global namespace 

    Type-checking 

    No type-checking capabilities 

    Robust typing support via imports

    Dependency Packaging 

    Packaging up functionality into small pieces 

    Declare dependencies between modules 

    File Structure 

    Flat files 

    References to other modules 

    Export 

    Exports in the same file 

    Exports scattered through codebase 

    Import 

    No import functionality 

    Must use a require statement to access exported functions and properties

    What is CommonJS in Node.js? 

    In Node.js each of the JavaScript files is handled as an individual separate CommonJS module. This infers that the variables, functions, classes, etc., which are present inside of it, are not, by default, directly accessible to other files. We need to explicitly describe the module system as to which parts of the code should be exported and which not. CommonJS modules are the original way to package JavaScript code for Node.js. 

    This loading paradigm was designed with server-side JavaScript in mind and is not suitable for client-side applications. Modules are loaded synchronously and are accessed in the same order that JavaScript locates them. 

    Symbols that are defined inside a JavaScript file are considered modules when they are exported. It could be variables, functions, objects, classes, etc. Let us take an example of string manipulation JavaScript file stringOps.js, which has several functions, like one to remove all spaces from the string. 

    exports.removeSpace = (str) => str.removeSpace() 

    Any other JavaScript file can then import and use this module, as follows: 

    const stringOpsModule= require('stringOps.js') 
    stringOpsModule.removeSpace('This is a test for whitespaces.') 

    This way of importing can be extended to import multiple values as well. Let's take an example of some other functions as well 

    exports.nextLine = ‘\n’ 
    exports.singleWildCard = ‘?’ 
    exports.multiWildCard = ‘*’ 

    and then import them individually in other files as required using destructing assignment. Following is an example :

    const { nextLine, singleWildCard, multiWildCard } = require('./stringOps.js') 

    There is also a way to just export one specific value, as we can see in the example: 

    //example.js 
    module.exports = value 
    and import as follows using :
    const value = require('./example.js') 

    The same way, we can import and use modules from NPM packages (i.e. from the node_modules folder). Let’s take the example of chalk library 

    const chalk = require("chalk") // needs to be called after npm install 
    console.log(chalk.blue("Hello world printed in blue")); 

    What is ES Module in Node.js? 

    The ECMAScript modules (in short ES modules) is a JavaScript modules format which is the official standard format to package JavaScript code for reuse. The ES modules format generally offers an easier route to writing isomorphic JavaScript, which can run in the browser or on a server.

    When Node.js came into existence, the ES modules support didn't exist, and so Node.js decided to use CommonJS Modules. While CommonJS as an organization is no longer an active concern, Node.js and npm have evolved the specification to create a very large JavaScript ecosystem. Due to this the ES module got its first support starting from version v8.5.0. At this time we needed to use it with an experimental flag ‘--experimental-modules’. 

    But starting from version v13.2.0, the support has been stabilized, so this tag is not required anymore. As a result, the Node/npm ecosystem is extended to both server and client, and is growing rapidly. ES Modules are defined using a variety of import and export statements, but Node.js uses ES modules format if: 

    • The file extension for the module is ‘.mjs’ or the module's nearest parent folder has { "type": "module" } in its package.json :

    An easy way to tell Node.js to treat the modules in ECMAScript format is to use the .mjs file extension. 

    Let us take the following ES module example weekday-from-date.mjs exports a function weekdayFromDate(), which is used to determine the day of the week of an arbitrary date: 

    // weekday-from-date.mjs (ES Module) 
    const WEEKDAY = ['Monday', 'Tuesday', ‘Wednesday', 'Thursday', 
    'Friday', 'Saturday', 'Sunday']; 
    export function weekDayFromDate(date) { 
    if (!(date instanceof Date)) { 
    date = new Date(date); 
    } 
    return WEEKDAY[date.getWeekday()]; 
    } 
    // weekday.mjs (ES Module) 
    import { weekDayFromDate } from './weekday-from-date.mjs'; 
    const dateString = process.argv[2] ?? null; 
    console.log(weekDayFromDate(dateString)); 

    Now if we run weekday.mjs module in command line: 

    node ./weekday.mjs "2022-06-08" 

    Wednesday is printed in the terminal. 

    • The argument --input-type=module is present in the config, and the module's code is passed as a string using --eval="<module-code>" argument or from STDIN. By default .js files in Node.js are considered CommonJS modules. To make .js files as ES modules, we need to set the "type" field as "module" in the package.json. Following presents an example for the same :
    { 
    "name": "my-app", 
    "version": "1.0.0", 
    "type": "module", 
    // ... 
    } 

    Now all ‘.js’ files inside the folder containing such package.json execute as ECMAScript modules, and we don’t need to alter the filenames. Let’s take the example of the weekday-from-date module’s example here as well :

    Let's rename weekday-from-date.mjs to weekday-from-date.js and weekday.mjs to weekday.js and keep the import and export syntax the same. Then we set "type" field as "module" in the package.json, and we can see Node.js executes these modules as ECMAScript ones. 

    node ./weekday.js "2022-06-07" 

    Tuesday is printed in the terminal. 

    Above was an example for local imports, but we can use the same to import other ES modules. The specifier is the string literal representing the path from where to import the module. Like in the example below, 'path' is a specifier: 

    // 'path' is the specifier 
    import module from 'path' 

    There are 3 kinds of specifiers in Node.js :

    1. Relative: Importing a module using a relative specifier would resolve the path of the imported module relative to the current (importing) module location. Relative specifiers usually start with '.', '..', or './':

    // Relative specifiers: 
    import module1 from './module1.js'; 
    import module2 from '../folder/module2.mjs'; 

    When using relative specifiers, indicating the file extension (.js, '.mjs', etc.) is obligatory. 

    2. Bare: A bare specifier starts with a module name and imports modules from node_modules or the built-in Node.js modules. For instance, if we have installed the lodash-es package in node_modules, then we can access that module using a bare specifier:

    // Bare specifiers: 
    import lodash from 'lodash-es'; 
    import intersection from 'lodash-es/intersection'; 

    Using bare specifiers we can also import the Node.js built-in modules: 

    import fs from 'fs' 

    3. Absolute: An absolute specifier imports modules using an absolute path:

    // Absolute specifier: 
    import module from 'file:///usr/opt/module.js'; 

    Also you can check out the Full Stack online course to work and experience everything in action. 

    Difference Between CommonJS vs ES Modules in Node.js

    Check further differences between CommonJS and ES Modules along with the parameters:

    1. CommonJS vs ES Modules: Functionality 

    CommonJS modules are similar to ES modules, but there are a few key differences. First, CommonJS modules must be loaded from a module repository, such as npm.

    Second, CommonJS modules can only be accessed from within the context of a Node.js application. CommonJS modules do not have exports or prototypes like ES modules do. Finally, the require() function in CommonJS is not equivalent to the import() function in ES Modules.

    2. CommonJS vs ES Modules: Compilation 

    CommonJS is a module system inspired by the AMD API from Microsoft. It allows modules to be written in a similar way to AMD modules, with the main difference being that CommonJS modules can be loaded into any node.js environment.

    ES Modules are a new module system developed by Google which allows for greater modularity and reuse across different programming languages. The advantage of using ES Modules is that they can be compiled into native code which makes them faster and more efficient than CommonJS modules.

    3. CommonJS vs ES Modules: Dependencies 

    CommonJS modules are AMD-compliant and can be used in both Node.js and browsers. ES modules are not AMD-compliant but are supported in browsers through the webpack module loader.

    CommonJS modules have a few dependencies that must be installed before they can be used: nodejs, npm, and yeoman. These dependencies can be installed using the following commands:

    npm install -g nodejs npm install -g npm yeoman 

    ES modules do not have any dependencies other than the module loader itself. However, some development tools may require additional libraries, such as Babel. To install these tools, you can use the following command: 

    yarn add babel babel-cli babel-preset-env

    4. CommonJS vs ES Modules: Typescript 

    CommonJS modules are a popular way to modularize JavaScript code. They allow you to export and import modules using the export and import keywords, respectively.

    ES Modules are a newer way to modularize JavaScript code. Unlike CommonJS modules, which rely on exports and imports, ES Modules use the module keyword. This allows you to define modules in a more declarative way without having to use the export and import keywords. Enroll in Full Stack online course to learn the essential modules in node js.

    5. CommonJS vs ES Modules: File Structure 

    The main difference between CommonJS and ES modules is the file structure. With CommonJS, all of the dependencies for a project are stored in one file called node_modules/. With ES modules, each dependency is stored in its files. This allows developers to better control how their dependencies are used and makes it easier to version software.

    6. CommonJS vs ES Modules: Export 

    CommonJS modules are a popular and efficient way to modularize code. They use the export keyword to make functions and variables accessible from other modules.

    ES modules are a newer version of the CommonJS module format. They use the export keyword but also include a require() function that automates the process of including other ES modules in your project.

    7. CommonJS vs ES Modules: Import 

    ES modules provide a way to group related code together for reuse. CommonJS, on the other hand, lets you use any module from the global namespace.

    To answer your question about CommonJS or ES modules which is better, CommonJS modules are typically used in Node.js applications, as they allow for a more centralized approach to code management and require no additional configuration on the part of the developer. 

    ES modules are popular in browser-based applications because they can be loaded asynchronously and don't require an underlying runtime environment like Node.js.

    Comparison Between CommonJS and ES Module in Node.js [With Example]

    Under the default scenario Node.js treats all JavaScript code as CommonJS modules. Because of this, CommonJS modules are characterized by the require() statement for module imports and module.exports for module exports. 

    Since by default, all the code is considered CommonJS, we need to explicitly mention the type specifying it by either the file extension(.mjs) in which case it then overrides the default standard of CommonJS and makes ES as default, or by using type:”module” in the package.json, which results in the same behavior.

    Since both types are different, it is only feasible to use one at a time. In contrast, conditional exports allow us to build libraries that are both backward-compatible with CommonJS modules and newer ES modules. Consider the following example 

    example-node-library 

    ├── lib/ 
    │ ├── module-exampleA.js (commonjs format) 
    │ ├── module-exampleA.mjs (es6 module format) 
    │ └── private/ 
    │ ├── module-exampleB.js (commonjs format) 
    │ └── module-exampleB.mjs (es6 module format) 
    ├── package.json 
    └── … 

    Inside package.json, we can use the exports field to export the public module (module-exampleA) in two different module formats while restricting access to the private module (module-exampleB): 

    // package.json 
    { 
    "name": "example-node-library",
    "exports": { 
    ".": { 
    "... 
    }, 
    "module-exampleA": { 
    "import": "./lib/module-exampleA.mjs" 
    "require": "./lib/module-exampleA.js" 
    } 
    } 
    } 

    Now that we have provided the following information about our example-node-library package, we can use it anywhere it is supported. 

    // For CommonJS 
    const module-exampleA = require('example-node-library/module-exampleA') 
    // For ES6 Module 
    import module-exampleA from 'example-node-library/module-exampleA' 

    Our public modules can be imported and required without specifying absolute paths because of the paths in different exports. Thus, by including '.js' and '.mjs', we are able to resolve the compatibility issue. Package modules can be mapped to different environments, such as a browser and Node.js, while private modules can be restricted. 

    We will be looking at the pros and cons of each in the next section. ES modules are the current standard for JavaScript, while CommonJS is the default standard in Node.js 

    Different Ways to Configure CommonJS Modules  

    Here are the key points regarding the configuration of CommonJS modules: 

    1. CommonJS Module FormatCommonJS specifies a module format using require() to load modules and module.exports or exports object to expose functionality. 

    2. Configuration Methods: 

    • Files with a .js Extension: When the nearest parent package.json file does not contain a top-level field "type". 
    • Files with a .js Extension and 'commonjs' Type: When the nearest parent package.json file contains a top-level field "type" with a value of "commonjs". 
    • Files with .cjs Extension: This is another way to configure CommonJS modules. 

    3. Pros and Cons: 

    • Synchronous Module Loading: CommonJS modules load synchronously, which can block the main thread of execution and cause delays, especially in large-scale applications. 
    • Legacy Code Support: CommonJS has full and stable support in legacy code or older versions of Node.js, particularly versions preceding v8.5.0. 

    4. ConclusionCommonJS modules, being the traditional method for handling dependencies in Node.js, offer a straightforward approach but may have performance limitations due to synchronous loading. 

    The choice between Common js and ECMAscript modules depends on the specific needs and requirements of the project

    Different Ways to Configure ECMAScript Modules 

    Configuring ECMAScript (ES) modules in Node.js involves several approaches, each catering to different project needs and setups: 

    1. File Extension Method: Using the .mjs extension for your JavaScript files signals Node.js to treat these files as ES modules. This method is straightforward and doesn't require any additional configuration. You simply name your files with .mjs instead of the traditional .js. 

    2. Package.json "type" Field: By setting the "type" field in your package.json to "module", Node.js treats all .js files in your project as ES modules. This approach is beneficial for projects entirely based on ES modules, as it avoids the need to change file extensions. 

    3. Hybrid Approach with Conditional Exports: For projects that need to support both CommonJS and ES modules, Node.js allows conditional exports in the package.json. Using the exports field, you can define different entry points for CommonJS (require()) and ES modules (import). This approach provides flexibility for libraries aiming to be compatible with various module systems. 

    4. Inline or Dynamic Import Statements: Node.js supports dynamic import() statements, which can be used to load ES modules conditionally or on demand. This feature is particularly useful when dealing with large applications or when modules need to be loaded based on certain conditions at runtime. 

    5. Transpilers and Bundlers: Tools like Babel or Webpack can be configured to transpile and bundle ES modules for compatibility with different environments.  

    These tools offer additional features like minification optimization and support for features not yet natively supported in node js each of these methods has its use cases and advantages. 

    The choice largely depends on project requirements, compatibility needs, and development workflows. Understanding these options allows developers to effectively leverage the power of es modules in their node js applications 

    Pros and Cons of CommonJS and ES Modules in Node.js

    A. CommonJS

    1. Support for Older Versions 

    In case of legacy code or older version of node, CommonJS has full and stable support, since it is the default standard. Versions preceding v8.5.0 have to be using this only. Also, upto very recently until v13.2.0, ES was marked experimental, there also CommonJS has a great support. 

    2. Flexibility with Module Imports 

    Import statements are allowed only at the beginning of the file if we call them elsewhere. The control moves the expression to the beginning of the file or throws an error. As a result, require() can be called anywhere in the code since it is parsed at runtime. As well as loading modules conditionally or dynamically, it can also load modules from if statements, conditional loops, or functions, e.g :

    if(str.length > 0) { 
    const StringMeta = require(‘./stringMeta.js’); 
    … 
    } 

    Here, we load the module stringMeta only if the string is non-empty. 

    3. Module Load is Synchronous

    One of the drawbacks of using require() is that modules are loaded and processed sequentially one by one since it does the loading synchronously. This can pose rigorous performance issues for large-scale applications that can load hundreds of modules. It might not be a problem for a small-scale application using a limited number of modules. 

    B. ES Module 

    1. Allows Import of CommonJS from ES

    NodeJS allows us to import CommonJS modules from ES Modules, since in this case, module. exports simply become the default export which we might import as such. 

    2. Can be Executed at Both Parse Time and Runtime 

    It is for this reason that imports are "hoisted", as they are implicitly moved to the top. The import syntax cannot be used anywhere in the code, therefore. The benefits of this are that errors can be caught upfront and we get better support from developer tools for writing valid code. However, using the import() function can give us the same benefit of dynamic loading. 

    3. Load from URL Support 

    ECMAScript 6 also provides support that modules can be loaded from a URL. This new improvement not only makes loading more complicated but also slow.

    4. Requires File Extension 

    A file extension must be provided when using the import keyword. Directory indexes (e.g., './directory/index.js') must be fully specified. 

    5. Is Asynchronous 

    Since the load is asynchronous, it makes more sense to use it to load many modules. 

    Looking to kickstart your coding journey? Discover the best online Python course for beginners! Unleash your potential with Python's simplicity and versatility. Join now and unlock endless possibilities.

    Which One is Better, CommonJS or ES Module? 

    Both CommonJS and ES Module are valid options; as we have just seen, it has evolved a lot in the past decade, and we have options for a good amount of versatility. But mostly, we can classify the decision-making into broadly two clauses :

    • Since ES Modules have been standardized for many years, it is often better to use them when starting a new project. Since the release of version 14, released in April 2020, NodeJS has had stable support for this. There is a lot of documentation and examples, and it is also interoperable with CommonJs. ES Modules support has already been added to many libraries by new package maintainers. 
    • If we are maintaining an existing NodeJS project using CommonJS, or if we are using an older version of Node.js, things may be different. It is good news that no existing code needs to be migrated at the moment. NodeJS still uses CommonJS as its default module system and it is unlikely to change any time soon. A conversion to the ES modules would also make the application incompatible with earlier versions of Node.js because of the sketchy support. 

    Conclusion

    When considering the transition from CommonJS to ES Modules in Node.js, it's advantageous to adopt a gradual migration approach. Tools such as Babel or TypeScript facilitate this process, enabling seamless integration of both module systems. By leveraging these tools, developers can maintain compatibility with existing CommonJS code while gradually incorporating ES Modules, offering flexibility for future updates. Remember, for further insight into the action, opt for KnowledgeHut Node.js best course to polish your skills on CommonJS vs ES Modules in Node.js. To know how to install and manage Node.js check this- Installing and Managing Node.js Using NVM

    Frequently Asked Questions (FAQs)

    1What is the major difference between CommonJS and ES module?
    • CommonJS is synchronous whereas ES is asynchronous
    • CommonJS supports only runtime resolution whereas ES supports both parse time and runtime
    • ES supports interoperability whereas CommonJS doesn't 
    • CommonJS is the default standard whereas ES recently achieved stable support
    • Common JS uses require() on the other hand ES uses import export
    2What are the benefits of CommonJS?

    CommonJS is the default standard and is supported in all Node.js versions. It is resolved at Runtime and has a wide amount of Dev support since it has been there since the beginning of Node.JS

    3Does Node.js use CommonJS?

    Yes, CommonJS is the default Standard for module inclusion. 

    4What is ES module in Node.js?

    In a browser the execution of JavaScript modules depends upon import and export statements. These statements load and export ECMAScript modules, which are the ES modules. The ES module usage format is the official standard to write JavaScript for maximum reusability and is what most web browsers natively support.

    5What is CommonJS module in Node.js?

    CommonJS module system is the default module system within the NodeJS ecosystem. CommonJS modules are the original way to package JavaScript code for Node.js. In terms of the loading paradigm for CommonJS, modules are loaded synchronously and are processed in the same order that the JavaScript runtime locates them. 

    Profile

    Gaurav Roy

    Author

    I am an avid coder, software developer by profession and computer science post graduate from IIT(ISM) Dhanbad. I have 6.5+ yrs of development experience, working with cross platform mobile development in both iOS, Android and Web. I love to code and design robust systems, exploring and exploiting various cutting edge tech stacks available including Artificial Intelligence/machine Learning and evolutionary computing technologies, my post graduate thesis being based on the field. Apart from academics, I am a guitar player and singer.

    Share This Article
    Ready to Master the Skills that Drive Your Career?

    Avail your free 1:1 mentorship session.

    Select
    Your Message (Optional)

    Upcoming Web Development Batches & Dates

    NameDateFeeKnow more
    Course advisor icon
    Course Advisor
    Whatsapp/Chat icon