As a JavaScript codebase grows, splitting it into separate files (modules) keeps each piece focused on a single responsibility. This lesson introduces the ES module syntax ā using export to expose a function or value from one file and import to bring it into another.
1Organization in JavaScript | Web Dev - In-Depth Guide Part 1
Modular code is cleaner, easier to test, and allows for better collaboration. Each module should have a single responsibility.
// utils.js
export const add = (a, b) => a + b;
// app.js
import { add } from './utils.js';2Step-by-Step Breakdown
Modular code is cleaner, easier to test, and allows for better collaboration. Each module should have a single responsibility.
Level Up š
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Splitting UI Code Into Modules Should Not Split Apart Related ARIA Logic
When a component's markup, its event handlers, and its ARIA attribute updates get spread across different modules without a clear contract between them, it's easy for one file to change without the others staying in sync ā silently breaking accessibility. Keep the ARIA-updating logic co-located with (or clearly imported alongside) the interaction logic it corresponds to.
SEO Implications
- 1
Module Bundling Choices Affect How Much JavaScript a Crawler Must Execute
Poorly organized modules with circular or unnecessary imports can bloat the final bundle a browser (or crawler) has to download and execute before content renders. Well-scoped, single-responsibility modules make it easier for bundlers to tree-shake unused code, keeping the JavaScript payload smaller.
Best Practices
Give Each Module a Single, Clear Responsibility
A file that exports unrelated utilities, API calls, and UI logic all at once becomes hard to test and reuse. Group related exports together (e.g. a dedicated `api.js`, a dedicated `formatters.js`) so each module has one clear reason to change.
Prefer Named Exports for Multiple Values, Default Export for a Module's Main Purpose
Named exports (`export const add = ...`) make it clear exactly what's being imported at the call site and support better auto-import tooling. Reserve a default export for a module whose entire purpose is that one thing, like a single component or class.
Frequent Bugs
`SyntaxError: Cannot use import statement outside a module` when running a script.
ES module syntax (import/export) only works in files the browser or Node.js treats as a module ā in the browser, the `<script>` tag needs `type="module"`; in Node.js, the file needs a `.mjs` extension or `"type": "module"` in package.json.
Real-World Examples
Splitting Shared Logic Into a Reusable Utility Module
Several files in a project needed the same date-formatting logic, which was originally copy-pasted in three places. Moving it into a single shared module meant a bug fix only had to happen once.
// formatters.js
export function formatDate(date) {
return new Intl.DateTimeFormat('en-US').format(date);
}
// dashboard.js
import { formatDate } from './formatters.js';
console.log(formatDate(new Date()));