DevAcademy
LearnJavaScriptES Modules (import/export)
IntermediateJavaScript

ES Modules (import/export)

Learn how to split JavaScript code across multiple files using ES modules — named exports, default exports, import syntax, and how ES modules compare to CommonJS.

Reading Time

16 min

Lesson

Lesson 28 of 48

Why Modules?

As programs grow, keeping everything in one file becomes unmanageable — variable names collide, related code gets scattered, and reasoning about what depends on what becomes difficult. Modules let you split code across multiple files, each with its own private scope, and explicitly declare what it shares with the outside world using export, and what it needs from other files using import.

Named Exports

A named export lets a module share zero, one, or many values under specific names. You can export a value directly at its declaration with the export keyword, or declare everything first and export a group of names together in a single export statement at the bottom of the file. Named exports must be imported using the exact same name they were exported with (unless you rename them, shown shortly).

math.js — Named Exports

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

Importing Named Exports

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

Default Exports

A module can also have one default export — the single "main" thing that module is considered to provide. Unlike named exports, a default export is imported without curly braces, and the importing file can call it whatever name it wants, since there's no exported name to match. A file can mix a default export with named exports, though it's common for small, focused modules to use only a default export.

user.js — Default Export

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

Importing a Default Export

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

Renaming Imports and Namespace Imports

If an imported name would clash with something already in scope, use the as keyword to rename it during import. If you want access to every named export from a module without listing each one, import * as someName gathers them all into a single object, with each export available as a property on it.

Renaming and Namespace Imports

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

Modules Are Automatically Strict and File-Scoped

Every ES module runs in strict mode automatically — you never need to write "use strict" yourself. Modules also get their own top-level scope: a variable declared at the top of one module is NOT visible in another module unless it's explicitly exported and imported. This is a big improvement over plain scripts, where every top-level variable used to pollute one shared global scope.

Static Import vs. Dynamic import()

The import statement you've seen so far is static — it's hoisted, resolved at load time, and must appear at the top level of a file, not inside an if statement or function. Dynamic import(), by contrast, is a function-like expression you can call anywhere in your code. It returns a Promise that resolves to the module, making it useful for loading code lazily — for instance, only when a certain feature is actually used.

Dynamic import()

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

ES Modules vs. CommonJS

FeatureES ModulesCommonJS
Export syntaxexport / export defaultmodule.exports / exports.name
Import syntaximport ... from "..."require("...")
LoadingStatic, resolved at parse time (plus dynamic import())Dynamic, resolved at runtime
Strict modeAutomaticNot automatic
Where usedModern browsers and modern Node.jsOlder and current Node.js codebases

You Will See Both in the Wild

Plenty of existing Node.js code, libraries, and tutorials still use CommonJS's require() and module.exports rather than import/export. The two systems solve the same problem — sharing code between files — with different syntax and different loading behavior (CommonJS is synchronous and dynamic, ES modules are primarily static). Modern JavaScript and modern Node.js favor ES modules, but recognizing CommonJS is still an essential, practical skill.

One Default Export per Module

A module can only have a single default export, but as many named exports as you like. A common convention is to default-export the one primary thing a file provides (a component, a class, a main function) and use named exports for smaller, secondary helpers that file also wants to share.

Import Paths and File Extensions

Relative import paths must start with ./ or ../ — a bare name like "math" is reserved for packages resolved from node_modules, not your own files. Depending on your build tooling, you may also need to include the file extension (like ./math.js) since not every environment resolves extensions automatically the way bundlers often do.

Interview Questions

Quick Quiz

1. How many default exports can a single module have?

2. How do you import a default export named User from "./user.js"?

3. What keyword lets you rename an imported binding?

4. What does dynamic import() return?

5. Which statement about ES modules is true?