JavaScript Modules: How Import and Export Work
The JavaScript module system, officially known as ECMAScript Modules
(ESM), provides a standardized mechanism for splitting code into
separate, reusable files using the import and
export statements. This article breaks down how this system
operates, covering the syntax for named and default exports, the
mechanics of static and dynamic imports, and the underlying three-phase
lifecycle the browser or runtime uses to load and execute modular
code.
Fundamentals of ES Modules
In the ES module system, each JavaScript file represents a distinct
module. Code inside a module runs automatically in strict mode
("use strict"), and variables declared at the top level are
scoped locally to that file rather than polluting the global scope. To
share functionality—such as functions, objects, or primitive
values—across files, you must explicitly export them from the source
file and import them into the destination file.
Exporting Functionality
JavaScript provides two primary ways to expose module features: named exports and default exports.
1. Named Exports
Named exports allow a single module to export multiple values by their identifiers. You can declare them inline or group them at the bottom of the file.
// Inline named exports
export const apiKey = "XYZ123";
export function calculateTotal(a, b) {
return a + b;
}
// Grouped named exports
const user = "Alex";
const role = "Admin";
export { user, role };2. Default Exports
Each module can have at most one default export. This is commonly used when a file is designed to export a single primary entity, such as a class or component.
// Default export
export default function logger(message) {
console.log(`[Log]: ${message}`);
}Importing Functionality
The import statement retrieves bindings exported by
other modules.
1. Importing Named Exports
Named imports must match the exact names of the exported identifiers and be wrapped in curly braces:
import { apiKey, calculateTotal } from './config.js';You can also rename imports using the as keyword to
prevent naming collisions:
import { calculateTotal as add } from './config.js';2. Importing Default Exports
Default exports are imported without curly braces and can be assigned any local name:
import customLogger from './logger.js';3. Namespace Imports
To import all named exports from a file as a single object, use the
* as syntax:
import * as Config from './config.js';
console.log(Config.apiKey);How the Module System Operates Under the Hood
The JavaScript engine executes modules in a three-phase process before any code runs:
- Construction (Parsing and Fetching): The engine
resolves the file path, downloads the module file, and parses it into a
data structure known as a Module Record. It statically scans for all
importandexportstatements to build a complete dependency graph without running the code. - Instantiation: The engine allocates memory spaces for all exported values and maps the imported bindings directly to those memory locations. Unlike older module patterns (such as CommonJS), ES Modules create live bindings. If a module updates an exported variable internally, the importing module immediately observes the updated value.
- Evaluation: The engine executes the module code from the bottom of the dependency tree upward, filling the allocated memory spaces with real values. Each module is evaluated only once; subsequent imports reference the cached result.
Static vs. Dynamic Imports
Standard import declarations are static. They must
appear at the top level of a file and cannot be nested inside loops,
conditionals, or functions. This rigidity enables build tools and
engines to optimize code via dead-code elimination (tree shaking).
For use cases requiring on-demand loading, JavaScript provides the
dynamic import() function. It returns a Promise that
resolves to the requested module:
if (userIsAdmin) {
import('./adminDashboard.js')
.then((module) => {
module.renderDashboard();
})
.catch((err) => {
console.error("Failed to load module", err);
});
}Dynamic imports are ideal for code splitting, performance optimization, and conditional resource loading.