Understanding Javascript imports and exports

Hussein Sarea

Hussein Sarea jul 16, 2022

5 min read 987 words

Javascript

Introduction

If you’ve spent any time working with Javascript over the past 5 or so years, you’ve probably come across Javascript Imports and Exports. Introduced as part of the ECMAScript 6 (ES6) specification in June 2015 and now supported in all major browsers, Imports and Exports have shaped the way we build modern Javascript applications. In the beginning, Javascript wasn’t designed to be modular. However, 2009 saw the release of Node JS, a popular JavaScript runtime environment that runs outside the browser. Node JS soon began supporting modularity through the CommonJS require function and with this developers began searching for solutions to bring modularity to Javascript in the browser.

What is modularity in Javascript

A Javascript module is a file that can export variables, functions, and classes that can be imported anywhere, allowing developers to share code across multiple files or projects.

Exporting from Javascript modules

Exporting variables, functions, and classes is made simple in Javascript with the export keyword, allowing developers to specify which data/functionality they would like accessed outside the module. Take the following example of a Dice module:

javascript

dice.js

const NUMBERS = [1, 2, 3, 4, 5, 6];
function min() {
    return Math.min(...NUMBERS);
}
function max() {
    return Math.max(...NUMBERS);
}
export function roll() {
    return Math.floor((Math.random() * max()) +  min());
}

The Dice module exports a single function, rollDice(), making it the only data/functionality available to be imported elsewhere. In some cases, the developer may decide that more than one piece of data/functionality, or even all data/functionality should be available outside of the Dice module. In this case, the developer could simply prefix each variable and function with the export keyword:

javascript

dice.js

export const NUMBERS = [1, 2, 3, 4, 5, 6];
export function min() {
    return Math.min(...NUMBERS);
}
export function max() {
    return Math.max(...NUMBERS);
}
export function roll() {
    return Math.floor((Math.random() * max()) +  min());
}

Now every variable and function in the Dice module can be imported individually elsewhere. However, in cases where there is lots of data/functionality to be imported, importing everything individually can become frustrating. Thankfully Javascript also provides developers with the default keyword. Using the default keyword, developers can specify a default export. In the Dice module, the developer could create a default export to make all of the module data/functionality available elsewhere with an import-defined prefix:

javascript

dice.js

export default {
    NUMBERS,
    min,
    max,
    roll
}

Importing from Javascript modules

What and how a developer imports data/functionality from a Javascript module depends entirely on what data/functionality the developer would like to make use of and how the module exports that data/functionality. In the Dice module, there is individually exported data/functionality and a default export.

Importing a default export

The syntax for importing default exports in Javascript is simple. If the developer was to import the default export from the Dice module it may look like this:

javascript

dice.js

import Dice from './modules/dice.js';

Note - ./modules/dice.js is the path to the file.

The developer could now reference the default export as Dice, giving them access to everything in the default export object as: Dice.NUMBERS, Dice.min(), Dice.max(), Dice.roll().

To prevent potential name clashes, Javascript allows default exports to be imported under any valid name. For example, the Dice module could be imported as Foo:

javascript

dice.js

import Foo from './modules/dice.js';

The developer could now reference the default export as Foo, giving the them access to everything in the default export object as: Foo.NUMBERS, Foo.min(), Foo.max(), Foo.roll().

Importing an individual data/ functionality

In some cases a module may not provide a default export, the default export may not provide the data/functionality the developer wishes to use, or default export may return lots of redundant data/functionality. For these situations, Javascript allows the developer to import individual data/functionality. To import individual data/functionality, the developer should wrap the desired exported data/functionality in curly brackets. For example, if the developer would like to import the NUMBERS and the roll() data/functionality from the Dice module:

javascript

dice.js

import { NUMBERS, roll } from './modules/dice.js';

This would give the developer access to the NUMBERS and roll() data/functionality as NUMBERS and roll(), exactly the names given to them by the module they were exported from. There are times when it's not suitable to use the names given by the module the developer is importing from are not suitable. When importing from multiple modules, the chances of name clashes become increasingly likely, the given names may not be descriptive enough. For these situations, Javascript provides the as keyword. The as keyword can be used to rename imported individual data/functionality. For example, should the developer want to import the NUMBERS data as DICE_NUMBERS and the roll() functionality as rollDice:

javascript

dice.js

import { NUMBERS as DICE_NUMBERS, roll as rollDice } from './modules/dice.js';

This would give the developer access to the NUMBERS and roll() data/functionality as DICE_NUMBERS and rollDice().

Importing and namespacing all individual data/functionality

In some cases, the developer may wish to import all available individual data/functionality from a module, without listing out every individual export. Not all modules will export all of that data/functionality as a default export. In these cases the developer can import all module exports under a namespace, using the * key. For example, if the developer wished to import all the Dice module data/functionality under the namespace Bar:

javascript

dice.js

import * as Bar from './modules/dice.js';

This would give the developer access to the NUMBERS, min(), max(), and roll() data/functionality as Bar.DICE_NUMBERS, Bar.min(), Bar.max(), and Bar.roll().

Conclusion

Javascript modularity has changed the way developers build web applications. There are several ways to import data/functionality from a module, hopefully, this post provides an insight into when and why to use each import syntax.

Share on Social Media: