Introduction to ES Modules
ES Modules is the ECMAScript standard for working with modules. While Node.js has been using the CommonJS standard since years, the browser never had a module system, as every major decision such as a module system must be first standardized by ECMAScript and then implemented
- Introduction to ES Modules
- The ES Modules Syntax
- Other import/export options
- CORS
- What about browsers that do not support modules?
- Conclusion
Introduction to ES Modules
ES Modules is the ECMAScript standard for working with modules.
While Node.js has been using the CommonJS standard for years, the browser never had a module system, as every major decision such as a module system must be first standardized by ECMAScript and then implemented by the browser.
This standardization process completed with ES6 and browsers started implementing this standard trying to keep everything well aligned, working all in the same way, and now ES Modules are supported in Chrome, Safari, Edge and Firefox (since version 60).
Modules are very cool, because they let you encapsulate all sorts of functionality, and expose this functionality to other JavaScript files, as libraries.
The ES Modules Syntax
The syntax to import a module is:
import package from 'module-name'
while CommonJS uses
const package = require('module-name')
A module is a JavaScript file that exports one or more values (objects, functions or variables), using the export
keyword. For example, this module exports a function that returns a string uppercase:
uppercase.js
export default str => str.toUpperCase()
In this example, the module defines a single, default export, so it can be an anonymous function. Otherwise it would need a name to distinguish it from other exports.
Now, any other JavaScript module can import the functionality offered by uppercase.js by importing it.
An HTML page can add a module by using a <script>
tag with the special type="module"
attribute:
<script type="module" src="index.js"></script>
Note: this module import behaves like a
defer
script load. See efficiently load JavaScript with defer and async
It’s important to note that any script loaded with type="module"
is loaded in strict mode.
In this example, the uppercase.js
module defines a default export, so when we import it, we can assign it a name we prefer:
import toUpperCase from './uppercase.js'
and we can use it:
toUpperCase('test') //'TEST'
You can also use an absolute path for the module import, to reference modules defined on another domain:
import toUpperCase from 'https://flavio-es-modules-example.glitch.me/uppercase.js'
This is also valid import syntax:
import { toUpperCase } from '/uppercase.js'
import { toUpperCase } from '../uppercase.js'
This is not:
import { toUpperCase } from 'uppercase.js'
import { toUpperCase } from 'utils/uppercase.js'
It’s either absolute, or has a ./
or /
before the name.
Other import/export options
We saw this example above:
export default str => str.toUpperCase()
This creates one default export. In a file however you can export more than one thing, by using this syntax:
const a = 1
const b = 2
const c = 3
export { a, b, c }
Another module can import all those exports using
import * from 'module'
You can import just a few of those exports, using the destructuring assignment:
import { a } from 'module'
import { a, b } from 'module'
You can rename any import, for convenience, using as
:
import { a, b as two } from 'module'
You can import the default export, and any non-default export by name, like in this common React import:
import React, { Component } from 'react'
You can see an ES Modules example here: https://glitch.com/edit/#!/flavio-es-modules-example?path=index.html
CORS
Modules are fetched using CORS. This means that if you reference scripts from other domains, they must have a valid CORS header that allows cross-site loading (like Access-Control-Allow-Origin: *
)
What about browsers that do not support modules?
Use a combination of type="module"
and nomodule
:
<script type="module" src="module.js"></script>
<script nomodule src="fallback.js"></script>
Conclusion
ES Modules are one of the biggest features introduced in modern browsers. They are part of ES6 but the road to implement them has been long.
We can now use them! But we must also remember that having more than a few modules is going to have a performance hit on our pages, as it’s one more step that the browser must perform at runtime.
Webpack is probably going to still be a huge player even if ES Modules land in the browser, but having such a feature directly built in the language is huge for a unification of how modules work client-side and on Node.js as well.
→ I wrote 17 books to help you become a better developer:
- C Handbook
- Command Line Handbook
- CSS Handbook
- Express Handbook
- Git Cheat Sheet
- Go Handbook
- HTML Handbook
- JS Handbook
- Laravel Handbook
- Next.js Handbook
- Node.js Handbook
- PHP Handbook
- Python Handbook
- React Handbook
- SQL Handbook
- Svelte Handbook
- Swift Handbook
Also, JOIN MY CODING BOOTCAMP, an amazing cohort course that will be a huge step up in your coding career - covering React, Next.js - next edition February 2025