How to Understand Modules in JavaScript
A step-by-step guide on how JavaScript modules work using ES Modules syntax, named and default exports, and module loading behavior.
Understand Why Modules Exist
Before modules, all JavaScript files shared the same global scope. Variables from one file could accidentally overwrite variables from another file. As applications grew, managing this shared global state became impossible. Modules solve this by giving each file its own private scope. Nothing in a module is accessible outside unless explicitly exported, and nothing from outside is accessible inside unless explicitly imported.
Understand ES Modules vs CommonJS
JavaScript has two module systems. CommonJS is the older system used in Node.js where you export with module.exports and import with require. ES Modules is the modern standard defined by ECMAScript, supported natively in browsers and in Node.js with the .mjs extension or type module in package.json. ES Modules use the export and import keywords. ES Modules are statically analyzed and support tree shaking. CommonJS is dynamic and evaluated at runtime.
Use Named Exports
Named exports allow a module to export multiple values. Place the export keyword before any declaration including variables, functions, and classes. A module can have any number of named exports. You can also collect them into a single export statement at the bottom of the file, listing all names to export inside curly braces. Named exports must be imported using the exact name they were exported with.
Use Default Exports
A module can have exactly one default export representing the main value the module provides. Use export default before a value, function, or class declaration. Default exports do not need a name at the point of export. When importing a default export, you choose any name you want for the imported value without using curly braces. A module can have both a default export and multiple named exports simultaneously.
Import Named and Default Exports
Import named exports using curly braces containing the exact exported names. Import a default export using any name without curly braces. Import both in the same statement by placing the default import name first, then a comma, then named imports in curly braces. Use the as keyword inside curly braces to rename a named import to avoid naming conflicts with existing variables in the importing module.
Understand Static Analysis and Tree Shaking
ES Module import and export statements are static, meaning they appear at the top level of modules and are evaluated before any code runs. This allows bundlers like Webpack and Vite to statically analyze the entire dependency graph and determine which exports are actually used. Any exported values that are never imported by any module can be safely removed from the final bundle, a process called tree shaking that reduces bundle size.
Understand Module Loading Behavior
Each ES Module is loaded, parsed, and evaluated only once regardless of how many other modules import it. If module A and module B both import module C, only one instance of C exists and its code runs only once. All importers receive a live binding to the same exported values. If an exported variable changes in the source module, all importing modules see the updated value automatically.
Use Dynamic Imports for Code Splitting
The static import statement must appear at the top level and loads modules upfront. The dynamic import function can be called anywhere, including inside conditions and functions, and returns a Promise that resolves to the module's exports. Use dynamic import to load heavy modules only when they are needed, such as when a user navigates to a specific page or opens a modal. This is the mechanism behind code splitting in modern bundlers.
Ready to master this completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.

