How to Import and Export in JavaScript with ES Modules
Modern JavaScript uses ES Modules (import/export) as the standard way to share code between different files. This system allows you to break your application into smaller, reusable pieces, which is essential for building scalable and maintainable projects.
This guide will teach you the fundamentals of the ES Module system. You will learn the difference between named and default exports, how to import classes, functions, and variables, and how to create "barrel" files to simplify your imports.
The Core Concepts: Named vs. Default Exports
There are two primary ways to export code from a module:
-
Named Exports:
- A module can have multiple named exports.
- You export by adding the
exportkeyword before a declaration. - You import them using their exact names inside curly braces (
{ }). - This is the recommended best practice for most cases because it is explicit and clear.
-
Default Export:
- A module can have only one default export.
- You export using the
export defaultkeywords. - You import them without curly braces, and you can give the import any name you want.
- This is best for modules that export a single, primary value, like a main class.
Exporting and Importing Multiple Values (Named Exports)
This is the most common and flexible pattern. It allows you to export multiple, distinct pieces of code from a single file.
For example, you have a utility file with several functions and constants that you want to use in another part of your application.
utils.js:
// Problem: How to export and import these specific items?
export const PI = 3.14159;
export function sum(a, b) {
return a + b;
}
export class User {
constructor(name) {
this.name = name;
}
}
Solution: use a named import to select exactly which pieces you need in another file.
main.js:
// Import only the `sum` function and the `User` class from utils.js
import { sum, User } from './utils.js';
const user = new User('Alice');
console.log(user.name); // Output: 'Alice'
const total = sum(10, 20);
console.log(total); // Output: 30
This is a named import. The names inside the { } must match the names that were exported.
Exporting and Importing a Single Value (Default Export)
A default export is useful when a file's primary purpose is to provide a single class or function.
For example, you have a module that defines a primary class, Employee.
Employee.js:
// Problem: Export this class as the "main" thing from this file.
export default class Employee {
constructor(name) {
this.name = name;
}
}
The solution uses a default import. You do not use curly braces, and you can choose any local name for the import.
main.js:
// Import the default export from Employee.js
import Employee from './Employee.js';
// You could also name it differently, though it's best to be consistent:
// import MyEmployee from './Employee.js';
const emp = new Employee('Bob');
console.log(emp.name); // Output: 'Bob'
Mixing Named and Default Exports
While it's often cleaner to stick to one style per file, you can mix both.
data.js:
export const version = '1.2.0'; // Named export
export default function processData(data) { // Default export
// ...
}
main.js:
// The default import comes first, followed by the named imports in braces.
import processData, { version } from './data.js';
console.log(`Using version ${version}`);
processData([]);
A Note on File Paths and the .js Extension
When importing, the path to the module ('./utils.js') is critical.
- It must be a relative or absolute path.
- In browsers, you must include the file extension (e.g.,
.js). - In Node.js, you can often omit the extension, but being explicit is a good practice.
Practical Example: Creating a Barrel File for Utilities
A "barrel" file is a module that imports from several other modules and then re-exports them from a single, convenient entry point. This is a common pattern for organizing a library or utility folder.
math/add.js:
export const add = (a, b) => a + b;
math/subtract.js:
export const subtract = (a, b) => a - b;
math/index.js (The Barrel File):
// Re-export the named exports from other modules
export { add } from './add.js';
export { subtract } from './subtract.js';
main.js:
Now you can import both functions from the single index.js barrel file instead of two separate files.
import { add, subtract } from './math/index.js';
console.log(add(10, 5)); // Output: 15
console.log(subtract(10, 5)); // Output: 5
Conclusion
ES Modules are the standard for structuring modern JavaScript applications.
- Named exports (
export const ...) are the recommended default. They are explicit and allow you to export multiple values. Import them withimport { ... } from '...'. - Default exports (
export default ...) are useful for modules that have a single, primary export. Import them withimport MyName from '...'. - Understanding the difference is key to writing clean, modular, and maintainable code.