Exporting and Importing Using ES6 Modules

In JavaScript, a module is simply a reusable piece of code that can be imported and used in other parts of your code. This makes it easier to organise your code into smaller, more manageable pieces and allows you to share code between different parts of your application.
Before the introduction of modules in ES6, developers had to rely on various techniques like Immediately Invoked Function Expressions (IIFE), CommonJS, and AMD to achieve modularity. However, with the introduction of modules in ES6, developers can now use a standardised and more convenient way of organising and sharing code.
Exporting Modules
In order to make a module available for use in other parts of your code, you need to export it. In JavaScript, you can export any object, function, or variable by adding the export keyword before it. For example:
// maths.js
export function add(a, b) {
return a + b;
}
This is a very basic example where we've defined a function called add, and then used the export keyword to make it available for use in other parts of our application. You can also use the export keyword to export multiple objects, functions, or variables from the same module:
// maths.js
export function add(a, b) {
return a + b;
}
export const pi = 3.1416;Now our maths.js file has two available exports: the add function, and the constant pi, both exported from the same module.
Importing Modules
Now that we have some exports available in maths.js, it's time to import them and put them to use in another part of the application. We achieve this using the import keyword. For example, importing our add function from the example above would look something like this:
// app.js
import { add } from './maths.js';
console.log(add(2, 3)); //=> 5Here we've imported add from maths.js using the relative path ./maths.js, then used it to add 2 and 3. We can also import several named exports from that module:
// app.js
import { add, pi } from './maths.js';
console.log(add(2, 3)); //=> 5
console.log(add(2, pi)); //=> 5.1416Now we've imported both add and pi from maths.js, and used them together.
Default Exports
Whilst you can explicitly name your exports, you can also export a single default value from a module using the export default syntax:
// maths.js
export default function add(a, b) {
return a + b;
}In this separate example, export default makes add the default export from maths.js. We can choose a different local name when importing it:
// app.js
import addFunction from './maths.js';
console.log(addFunction(2, 3)); //=> 5Here, the default export from maths.js is imported as addFunction. There are no curly braces around a default import. A whole maths module would probably offer more than addition, but this keeps the example small.
Mixing Named and Default Exports
You can ‑ of course ‑ also mix named and default exports in the same module. Here's an example:
// maths.js
export function add(a, b) {
return a + b;
}
export default function multiply(a, b) {
return a * b;
}This time we've exported a named function, add, and a default function, multiply, from maths.js. We can import and use both:
// app.js
import multiplyFunction, { add } from './maths.js';
console.log(multiplyFunction(2, 3)); //=> 6
console.log(add(2, 3)); //=> 5Issues with Modules
Whilst modules are a powerful and convenient feature of ES6, there are some potential issues you may come across when working with them. Here are a few of the more common ones:
Browser Compatibility
Not all browsers support ES6 modules (yet), so if you want to use them in your application you may need to use a bundler like webpack or Rollup to compile your code into a format that browsers can understand. As with any recent addition in front‑end development, it takes time for the browsers to catch up.
Circular Dependencies
ES modules can contain circular dependencies: module A may import from B whilst B imports from A. The risk is when a module reads a binding before it has been initialised, which can throw a ReferenceError. A loader alone does not fix that ordering problem. Prefer clearer dependency boundaries, or arrange for the value to be used only after both modules have initialised.
Naming Conflicts
If you're importing multiple modules that have objects or functions with the same name, you may run into naming conflicts. To avoid this, try to give your objects and functions unique names, or use namespace objects to group related objects and functions together.
You can also rename when importing from a module using the as keyword:
import { pi as piNumber } from './maths.js';Here we import the pi constant from maths.js but also renamed it to piNumber. This will avoid conflicts if you have another constant called pi in this particular area of your application, but really you would be better off refactoring one or the other to avoid confusion.
The Wrap‑up
Modules are a powerful and convenient way to organise and share code in JavaScript. By using named exports, default exports, and a combination of both, you can easily create reusable pieces of code that can be imported and used in other parts of your application. Whilst there are some potential issues you may come across when working with modules, these can be easily overcome with a bit of planning and organisation. With modules, you can write cleaner, more modular code that's easier to maintain and share with others.