As web applications grow in size and complexity, managing your code efficiently becomes crucial. JavaScript modules allow you to split code into smaller, reusable pieces with clean separation of concerns. With ES6 (ECMAScript 2015), JavaScript introduced a standardized module system that is now widely supported in modern browsers and JavaScript environments.
In this seventh blog of the JavaScript Essentials series, weโll take a deep dive into ES6 modulesโwhat they are, how to use them, and how they help you write cleaner, scalable, and maintainable code.
1. What Are JavaScript Modules?
A module is a file that encapsulates related pieces of code. Variables, functions, and classes declared in one module are not globally accessibleโthey must be exported to be used in other modules.
Why use modules?
- Avoid global scope pollution
- Separate logic by responsibility
- Reuse code across files/projects
- Improve testability and maintainability
2. Exporting from a Module
There are two ways to export values: named exports and default exports.
Named Exports
// math.js
export const PI = 3.14;
export function add(x, y) {
return x + y;
}
Default Export
// greet.js
export default function(name) {
return `Hello, ${name}!`;
}
You can also export all at once:
const multiply = (a, b) => a * b;
const divide = (a, b) => a / b;
export { multiply, divide };
3. Importing Modules
Named Imports
import { PI, add } from './math.js';
console.log(add(3, PI));
Default Import
import greet from './greet.js';
console.log(greet("Alice"));
Renaming Imports
import { add as sum } from './math.js';
Import Everything
import * as MathUtil from './math.js';
console.log(MathUtil.PI);
4. Module Scope and Encapsulation
Modules help avoid variable leaks by default. Variables defined in a module file arenโt visible to other modules unless exported.
// module.js
let privateVar = 'hidden';
export const publicVar = 'visible';
Trying to access privateVar
outside will result in an error.
5. Using Modules in HTML
Use the type="module"
attribute when referencing module files in the browser.
<script type="module" src="main.js"></script>
Important notes:
- Module scripts are deferred automatically
- Scripts must use file paths (
./module.js
) - Cross-origin restrictions apply for loading modules
6. Live Bindings
Module exports are live bindingsโthey reflect the current value.
// counter.js
export let count = 0;
export function increment() {
count++;
}
// main.js
import { count, increment } from './counter.js';
increment();
console.log(count); // Output: 1
7. Dynamic Imports with import()
Dynamic imports allow loading a module conditionally or on demand.
if (shouldLoadFeature) {
import('./feature.js')
.then(module => module.init())
.catch(err => console.error(err));
}
This is useful for:
- Lazy loading
- Code splitting
- Loading large libraries on demand
8. Top-Level await
in Modules
As of ES2022, you can use await
at the top level in modules (not inside functions).
// data.js
const response = await fetch('/api/data');
const data = await response.json();
console.log(data);
Must be inside a module script (type="module"
).
9. Importing JSON and Non-JS Files
ES Modules in browsers donโt support importing JSON or CSS directly, but tools like Webpack, Vite, and Rollup do.
// With Webpack
import config from './config.json';
import './style.css';
10. Real Project Structure Example
Organize your code logically by feature or domain.
/src
โโโ components/
โ โโโ navbar.js
โ โโโ footer.js
โโโ utils/
โ โโโ math.js
โโโ main.js
navbar.js
export function createNavbar() {
const nav = document.createElement('nav');
nav.textContent = "Navigation";
return nav;
}
main.js
import { createNavbar } from './components/navbar.js';
document.body.appendChild(createNavbar());
11. Best Practices
โ
Export only whatโs needed
โ
Use named exports when exporting multiple utilities
โ
Keep modules small and focused
โ
Use consistent naming and folder structure
โ
Avoid circular dependencies
12. Common Pitfalls
โ Missing file extensions
import utils from './utils'; //
โ fails
import utils from './utils.js'; //
โ
โ Mixing CommonJS (require
) with ES Modules
โ Global variables in modules โ avoid polluting scope
13. Summary Table
Feature | Description |
export | Makes values accessible to other files |
import | Imports values from another module |
default export | A single main export per file |
named export | Allows multiple exports from one module |
type=\"module\" | Enables module use in HTML |
import() | Dynamic, asynchronous import |
Top-Level await | Use await directly in modules |
14. Sample Application Snippet
math.js
export function square(x) {
return x * x;
}
main.js
import { square } from './math.js';
console.log(`Square of 4 is ${square(4)}`);
index.html
<script type="module" src="main.js"></script>
15. Conclusion
JavaScript modules are a fundamental building block for scalable application development. By organizing code into reusable, well-structured modules, you enhance clarity, maintainability, and reusability across your project.
In the next blog, weโll cover Debugging and Error Handling in JavaScriptโincluding try...catch
, browser tools, stack traces, and strategies to write more robust, bug-resistant code.