← Back to JavaScript Course | Chapter 4: Modern JS, Async & DOM | Lesson 15 of 26

JS Modules

Think of a company with separate departments, accounting, marketing, shipping, each handling its own work independently, but sharing specific reports and resources with each other exactly when needed. JavaScript modules let you split code into separate files that work the same way, each file, or module, handles its own piece of logic and explicitly shares, or exports, only the parts other files actually need to import and use. As a project grows, modules keep code organized and manageable, exactly the kind of structure that helps larger applications, including ones behind cookiescursor.com, stay maintainable.

export and import Basics

export marks a function, variable, or class as available for other files to use, and import brings that exported item into a different file so it can be used there too.

Note: Think of export as 'making something public' from a file, and import as 'requesting something public' from another file.

Warning: A value that isn't explicitly exported stays completely private to its own file, other modules simply cannot access it.

Example: export and import Basics

javascript
// math.js
export function add(a, b) { return a + b; }

// main.js
import { add } from './math.js';
console.log(add(2, 3));

Default Exports

A default export represents the single main thing a module provides, and is imported without curly braces, using any name the importing file chooses. A file can only have one default export, which is why it's typically used for a module's primary function, class, or component.

Note: A file can have at most one default export, reserve it for the primary thing that file is responsible for providing.

Warning: Because the importing file can name a default import anything it wants, inconsistent naming across a project can make code harder to follow.

Example: Default Exports

javascript
// user.js
export default function createUser(name) { return { name }; }

// main.js
import createUser from './user.js';
console.log(createUser("Sam"));

Named Exports

Named exports let a single file export multiple distinct values, each imported individually by its exact exported name, wrapped in curly braces on the importing side. A module can freely mix multiple named exports alongside a single default export.

Note: Named exports are ideal for utility files providing several related, independent functions that consumers might import selectively.

Warning: Named imports must exactly match the exported name, unless explicitly renamed with the as keyword during import.

Example: Named Exports

javascript
// utils.js
export const PI = 3.14;
export function double(n) { return n * 2; }

// main.js
import { PI, double } from './utils.js';
console.log(PI, double(5));

Using type="module" in a Script Tag

Adding type=module to a script tag tells the browser to treat that script as an ES module, enabling import and export syntax, and automatically deferring the script until after the page has parsed.

Note: Module scripts are automatically deferred, similar to the defer attribute, so you don't need to add defer separately on a module script.

Warning: Module scripts require the page to be served over http:// or https://, opening an HTML file directly from the file system typically blocks module imports.

Example: Using type="module" in a Script Tag

javascript
<script type="module">
  import { add } from './math.js';
  console.log(add(1, 2));
</script>

Module Scope

Every module has its own private top-level scope, meaning variables and functions declared in one module file are not automatically visible in another, unless they're explicitly exported and imported.

Note: Module scope naturally avoids polluting the global scope, a major advantage over older approaches that relied on multiple plain script tags sharing one global namespace.

Warning: Two modules can safely declare variables with the exact same name without any conflict, since each module's top-level scope is completely separate.

Example: Module Scope

javascript
// counter.js
let count = 0; // private to this module, not visible elsewhere
export function increment() { return ++count; }
Common Mistakes
  1. Forgetting to add type=module to the script tag, without it, export and import statements throw a syntax error.
  2. Mixing up default exports and named exports, their import syntax is different and cannot be used interchangeably.
  3. Trying to load a module directly from the local file system without a server, browsers block module imports over the file:// protocol for security reasons.
Chapter Summary
  • export makes a function, variable, or class available to other files, and import brings it into another file that needs it.
  • A default export is the main thing a module provides, while named exports allow multiple distinct exports from one file.
  • Adding type=module to a script tag is required to use import and export, and modules automatically run in their own private scope.
Browser Support

Native ES modules with type=module have been supported in all major browsers since around 2018 and are now a fully standard part of the web platform.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.