*
Previous Nullish coalescing (??) Connecting JS with REST APIs Next

📦 JavaScript Modules

📘 What Are Modules?

A JavaScript module is a file that encapsulates related code — variables, functions, classes — and exports them for use in other files. Modules help organize code, improve reusability, and support maintainability in large applications.

JavaScript modules are self-contained, reusable blocks of code that help organize and structure large applications. Instead of writing a single, long script, you can divide code into separate files based on their functionality. This approach encapsulates logic, prevents naming conflicts, and improves code readability and maintainability.

🧱 Why Use Modules?

  • Encapsulation: Keeps code self-contained and avoids global scope pollution
  • Reusability: Share logic across multiple files or projects
  • Maintainability: Easier to debug and update modular code
  • Scalability: Supports large-scale application development

🔧 Types of Modules

  1. ES Modules (ESM) – Standard in modern browsers and tools
  2. CommonJS – Used in Node.js environments
  3. UMD (Universal Module Definition) – Works in both browser and Node.js

📦 ES Modules (ESM)

The standard module format in modern JavaScript

The standard module format in modern JavaScript is ES Modules, which uses import and export statements. ES modules are the recommended standard for both client-side and server-side development.

How to use ES modules

Export a module: Use the export keyword before a function, variable, or class to make it available to other modules.

Import a module: Use the import statement to bring exported functionality from another file.

Example: Named exports

You can have multiple named exports from a single module.

utils.js

//javascript
export const pi = 3.14;

export function add(a, b) {
  return a + b;
}

main.js

//javascript
import { add, pi } from './utils.js';

console.log(add(5, 3)); // Output: 8
console.log(pi);      // Output: 3.14

Exporting

// file: math.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;

// or default export
export default function multiply(a, b) {
  return a * b;
}

Importing

// file: app.js
import { add, subtract } from './math.js';
import multiply from './math.js';

console.log(add(2, 3));       // 5
console.log(multiply(4, 5));  // 20

📦 CommonJS (Node.js)

// file: math.js
const add = (a, b) => a + b;
module.exports = { add };

// file: app.js
const { add } = require('./math');
console.log(add(2, 3)); // 5

Example: Default exports

You can have only one default export per module. When importing, you can give it any name you like.

math.js

//javascript
export default function multiply(a, b) {
  return a * b;
}

main.js

//javascript
import myMultiplyFunction from './math.js';

console.log(myMultiplyFunction(4, 5)); // Output: 20

In the browser

To enable module functionality, include the type="module" attribute in your <script> tag.

html
<script type="module" src="main.js"></script>

On the server (Node.js)

Node.js supports ES modules natively. To use them, you must either:

  • Use the .mjs file extension.
  • Add "type": "module" to your package.json file.

Dynamic imports

Introduced to load modules asynchronously and on-demand, dynamic imports use the import() function syntax, which returns a promise.

Syntax

//javascript
const myModule = await import('./myModule.js');
myModule.myFunction();

Use cases

  • Code splitting: Reduce the initial bundle size by loading less critical code only when it's needed.
  • Conditional loading: Load a module only if a certain condition is met.

Legacy module systems

CommonJS

Before ES modules were standardized, CommonJS was the standard for module management in Node.js.

Exports: Uses module.exports to export values.

Imports: Uses require() to import modules.

Loading: Modules are loaded synchronously, which can block execution.

utils.js (CommonJS)

//javascript
function add(a, b) {
  return a + b;
}
module.exports = add;

main.js (CommonJS)

//javascript
const add = require('./utils.js');
console.log(add(5, 3)); // Output: 8

Best practices and advantages

  • Prevent naming conflicts: Modules create a private scope for variables, classes, and functions, so they don't pollute the global namespace.
  • Improve maintainability: Smaller, focused files make your code easier to debug, read, and manage.
  • Encapsulation: Modules expose only the necessary parts of their code through exports, keeping the implementation details private.
  • Code reusability: A module can be imported and reused anywhere in your application or even across different projects.
  • Enable tree-shaking: The static nature of ES modules allows bundlers like webpack to analyze dependencies at compile time and remove any unused code, leading to smaller final bundles.

✅ Advantages

  • Improves code structure and separation of concerns
  • Enables lazy loading and tree-shaking for performance
  • Supports dependency management

❌ Disadvantages

  • Requires build tools or transpilers in older environments
  • Mixing module systems (e.g., ESM and CommonJS) can be tricky

🧠 Best Practices

  • Use named exports for clarity; default exports for single-purpose modules
  • Keep modules focused on a single responsibility
  • Use relative paths for local modules and bare imports for packages
  • Organize modules into folders by feature or domain

💡 Tips

  • Use type="module" in HTML to enable ESM:
    <script type="module" src="app.js"></script>
  • Use import() for dynamic imports:
    import('./math.js').then(module => {
      console.log(module.add(2, 3));
    });

🔗 References

Back to Index
Previous Nullish coalescing (??) Connecting JS with REST APIs Next
*
*