Import maps 101
Tags: beginners, tutorial, javascript, webdev
Published: 2023-01-10 20:15:04

As web developers, the use of external libraries saves a lot of time and effort, allowing us to build apps faster and more efficiently. In this article, I will be introducing you to a spec called [import maps](https://wicg.github.io/import-maps/), which we can use to control the behavior of importing these libraries.

## Introduction

Typically, using external libraries in browsers requires bundlers/build tools such as webpack or browserify, in order to parse the code and allow the use of import statements. The modules which are imported without an absolute or relative URL are known as bare modules. Such modules cannot be imported in the browser without bundle tools. For example, importing the lodash package would be written as:

```js
import _ from 'lodash';
```

This bare import specifier makes it easier to write and manage code, without having to write absolute or relative URLs of the library.

## What exactly does import maps solve?

With bundlers and build tools solving the issue with bare imports, you may now wonder what problem import maps actually solves.

The short answer is: **import maps can be used to load bare modules without the need for bundlers.**

The longer explanation is that normally, when code gets bundled together into a large file, import maps can be used to control the behavior of importing libraries. Examples of such behaviors we might want to control include remapping of URL-like specifiers, scoping, dynamic imports, etc. This control allows better caching for module script graphs and better developing performance.

## Example usage

Let’s take a look at an example implementation of import maps.
In this tutorial, we will learn how to import a bare module like [dayjs](https://day.js.org/) without the use of a bundler.

### Step 1. Create HTML and JS files

First, let’s create a new HTML page and a script file.

In the HTML file, let’s import a JavaScript file (still empty though) as a module like so:

```html
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Import maps demo</title>
  <script type="module" src="./index.js"></script>
</head>
<body>
</body>
</html>
```

### Step 2. Write a simple script in the JavaScript file

Now let’s write something simple that uses dayjs.
For this example, I am writing a script that prints out the current datetime every second:

```js
import dayjs from 'dayjs';

const el = document.createElement('h1');

document.body.appendChild(el);

setInterval(() => {
  el.innerHTML = dayjs(Date.now()).format('YYYY-MM-DD HH:mm:ss');
}, 1000);
```

### Step 3: Add an import map to import the library

If you try running the page now, it would not work because the dayjs we imported in the JavaScript file is a bare module.
You will see the following error:

> Uncaught TypeError: Failed to resolve module specifier "dayjs". Relative references must start with either "/", "./", or "../".

So let’s use import maps to resolve this error.
Change the HTML file in this way:

```html
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Import maps demo</title>

  <!-- Add this importmap before index.js -->
  <script type="importmap">
    {
      "imports": {
        "dayjs": "https://cdn.skypack.dev/dayjs@1.11.5"
      }
    }
  </script>

  <script type="module" src="./index.js"></script>
</head>
<body>
</body>
</html>
```


_Note: When implementing import maps, ensure that they are added before the first module load is started, in this example, index.js._

Now let’s run the page, and our script should be running with no issues:

![The time is ticking](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e3sqsov1vs7uhvcs54gz.gif)

## Scopes

Another important aspect to know about Import Maps is scoping, which allows the use of multiple versions of the same package in a specified scope.

For example, the code below specifies that any module in `/src` path will use the `https://cdn.skypack.dev/dayjs@1.10.7` package, while anything outside that path will use `https://cdn.skypack.dev/dayjs@1.11.5`.

```html
<script type="importmap">
  {
    "imports": {
      "dayjs": "https://cdn.skypack.dev/dayjs@1.11.5"
    },
    "scopes": {
      "/src": {
        "dayjs": "https://cdn.skypack.dev/dayjs@1.10.7"
      }
    }
  }
</script>
```

This is useful in the case where you need to work with multiple versions of the same package simultaneously for various reasons,
such as compatibility with other libraries, ensuring legacy systems can still run and so on.

## VS Code extension

Alternatively, instead of writing our import maps script, we could use the [VS Code extension](https://marketplace.visualstudio.com/items?itemName=JSPM.jspm-vscode) which can automatically generate, and inject import maps for modules and HTML pages.

## Other Usages

There are also many other uses of import maps besides allowing bare module imports such as:

- importing modules within modules;
- allow extension-less imports;
- remapping imports (mapping away hashes).

You may learn more about these useful features in the official documentation.

## Browser Support

In terms of browser support, at the time of this article, it is supported in Chromium-based browsers only (i.e. Chrome, Edge). More of this information can be found on the image below:

![Browser support table](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7dskpnbsv82duu5c4fsp.png)

## Polyfills

If you want import maps to be supported in any browser, there is an [ES Module Shims polyfill](https://github.com/guybedford/es-module-shims) which is compatible with any browser that has baseline ES Module Support (i.e. Edge 17+, Firefox 60+, Safari 10.1+, and Chrome 61+).

To use it, simply add the polyfill script with an `async` attribute, and add an import map or module script below:

```html
<!-- import polyfill here -->
<script async src="https://ga.jspm.io/npm:es-module-shims@1.5.16/dist/es-module-shims.js"></script>

<script type="importmap">
  {
    "imports": {
      "react": "https://ga.jspm.io/npm:react@18.0.0-rc.0/index.js"
    },
    "scopes": {
      "https://ga.jspm.io/npm:react@18.0.0-rc.0/": {
        "object-assign": "https://ga.jspm.io/npm:object-assign@4.1.1/index.js"
      }
    }
  }
</script>

<script type="module">
  import react from 'react';
  console.log(react);
</script>
```

The code example is taken from the [shim repo](https://github.com/guybedford/es-module-shims#usage).

## Conclusion

In this article, we learned about import maps, what problems they can solve, and how to implement them. Thanks for reading, I hope it has been a helpful article in getting you started with import maps!

You can read more about import maps in the References section below. Please share this article if it is insightful! Cheers!

## References

- [Import maps proposal](https://github.com/WICG/import-maps)
- [ES Module Shims](https://github.com/guybedford/es-module-shims)
- [CanIUse? import maps](https://caniuse.com/import-maps)
- [JSPM Generator](https://marketplace.visualstudio.com/items?itemName=JSPM.jspm-vscode) (VSCode extension)

_Originally published by Victoria Lo in [Uploadcare Blog](https://uploadcare.com/blog/import-maps-101/)._