An overview of ES6 Modules in JavaScript

Introduction

Until recently if you wanted to take full advantage of modules in JavaScript you needed to make use of third party libraries. Tools like CommonJS (AMD), Browserify, Webpack and others have become widely utilised in recent years. These tools are very powerful but can be complex to implement.

A simple and standardised module system has so far proven elusive in JavaScript. I think it's one of the most important features that has remained missing from the core language.

The good news is ES6 Modules will change all this and will be supported in all modern browsers sooner than you may think.

Browser Support

  • Safari 10.1+ — Support enabled by default
  • Firefox 54+ — Support enabled via about:config
  • Chrome 60+ — Support enabled via about:flags
  • Edge 15 — Support enabled via about:flags
  • Opera 47 — Support enabled via about:flags

Check out Can I use for the latest browser support information on ES6 modules.

As you can see, although not quite ready for prime time, ES6 modules will be available by default in all modern browsers very soon.

Using ES6 Modules

Here are some simple examples to demonstrate how to get started.

<!DOCTYPE html>
<html>
<head>
<title>ES6 module example</title>
<script src="app/utils.js" type="module"></script>
<script src="app/fallback.js" nomodule></script>
</head>
<body>
</body>
</html>

You can add module to the type attribute of a script element <script type="module">. The browser will then treat either inline or external script elements as an ES6 module.

Browsers that support ES6 modules will ignore scripts with a nomodule attribute. This is useful for providing a fallback script to legacy browsers.

It's worth noting that ES6 modules now have strict mode enabled by default.

Single Export

In utils.js file.

function hello() {  
    return "Hello"; 
}
function world() {  
    return "World";
}
// Basic export
export { hello, world };  

In main.js file.

import {hello, world} from '/app/utils.js';  
hello();  
world();  

Include a name space

import * as util from '/app/utils.js';  
util.hello();  
util.world();  
Named Export
export function hello() {  
    return "Hello"; 
}
export function world() {  
    return "World";
}

Named exports can also export objects/function as and when you cre­ate them.

Default Export

In utils.js file.

var utils = {  
    hello: function() {
        return "Hello";
    },
    world: function() {
        return "World";
    }
}
export default utils;  

In main.js file.

import utils from '/app/utils.js';  
utils.hello()  
utils.world()  

Gotchas

Be aware that there are a few differences between regular JS and JavaScript running in an ES6 module.

  • CORS — Module scripts are requested with CORS and should make use of valid CORS headers e.g. Access-Control-Allow-Origin: *.

  • Use root relative paths to import modulesimport {foo} from '/utils/bar.js'; works. import {foo} from 'utils/bar.js'; doesn't work.

  • Strict mode by default — ES6 modules run in strict mode (no 'use strict' is required).

  • The top level value of this is undefined — not the window object.

  • variables are local to the module — unless specifically exported.

  • Deferred by default — modules are non blocking and execute as defer scripts.

Further Information

Documentation from MDN and some more detailed examples: