Do you want to use ES Modules in your HTML files?

It’s easy! Just specify type="module" for the script element.

  • ./lib/hello.js
    const hello = () => alert("Hello!")
    export default hello
    
  • ./index.html
    <script type="module">
    import hello from "./lib/hello.js"
    hello()
    </script>
    

You also want to import and use React, right?

In that case, you need to find the URL for React distributed as ES Modules.

Hmmm… resolving dependencies seems to be a challenge too.

JSPM is the way to go!

JSPM has the import map generator. You can use it to specify the packages you want to use in your modules, and it will generate an import map including dependent modules.

https://generator.jspm.io/#U2NjYGBiDs0rySzJSU1hKEpNTC7RTcnPdTC00DPSM4AIQDkARfwM8iwA

The following template will be generated.

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Untitled</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
  <!--
    JSPM Generator Import Map
    Edit URL: https://generator.jspm.io/#U2NjYGBiDs0rySzJSU1hKEpNTC7RTcnPdTC00DPSM4AIQDkARfwM8iwA
  -->
  <script type="importmap">
  {
    "imports": {
      "react": "https://ga.jspm.io/npm:react@18.2.0/index.js",
      "react-dom": "https://ga.jspm.io/npm:react-dom@18.2.0/index.js"
    },
    "scopes": {
      "https://ga.jspm.io/": {
        "process": "https://ga.jspm.io/npm:@jspm/core@2.0.0-beta.27/nodelibs/browser/process-production.js",
        "scheduler": "https://ga.jspm.io/npm:scheduler@0.23.0/index.js"
      }
    }
  }
  </script>

  <!-- ES Module Shims: Import maps polyfill for modules browsers without import maps support (all except Chrome 89+) -->
  <script async src="https://ga.jspm.io/npm:es-module-shims@1.5.1/dist/es-module-shims.js" crossorigin="anonymous"></script>

  <link rel="modulepreload" href="https://ga.jspm.io/npm:@jspm/core@2.0.0-beta.27/nodelibs/browser/process-production.js"/>
  <link rel="modulepreload" href="https://ga.jspm.io/npm:react-dom@18.2.0/index.js"/>
  <link rel="modulepreload" href="https://ga.jspm.io/npm:react@18.2.0/index.js"/>
  <link rel="modulepreload" href="https://ga.jspm.io/npm:scheduler@0.23.0/cjs/scheduler.production.min.js"/>
  <link rel="modulepreload" href="https://ga.jspm.io/npm:scheduler@0.23.0/index.js"/>

  <script type="module">
    import * as react from "react";
    import * as reactDom from "react-dom";

    // Write main module code here, or as a separate file with a "src" attribute on the module script.
    console.log(react, reactDom);
  </script>
</body>
</html>

You will see that the import map contains react and react-dom dependency modules.

Finally, let’s write some code using React.

  • ./lib/Hello.jsx
    import React from "react"
    
    const hello = (props) => <p>Hello {props.name}!</p>
    
    export default hello
    
  • ./lib/Hello.js
    import React from "react";
    var Hello = (props) => /* @__PURE__ */ React.createElement("p", null, "Hello ", props.name, "!");
    var Hello_default = Hello;
    export {
      Hello_default as default
    };
    
    • You can compile it as follows
    npx esbuild lib/Hello.jsx --bundle --external:react --format=esm --outfile=lib/Hello.js
    
  • ./index.html (excerpt)
    ...
    
    <script type="importmap">
    {
      "imports": {
        "react": "https://ga.jspm.io/npm:react@18.2.0/index.js",
        "react-dom": "https://ga.jspm.io/npm:react-dom@18.2.0/index.js",
        "hello": "./lib/Hello.js"
      },
      "scopes": {
        "https://ga.jspm.io/": {
          "process": "https://ga.jspm.io/npm:@jspm/core@2.0.0-beta.27/nodelibs/browser/process-production.js",
          "scheduler": "https://ga.jspm.io/npm:scheduler@0.23.0/index.js"
        }
      }
    }
    </script>
    
    ...
    
    <div id="root"></div>
    
    <script type="module">
    import React from "react"
    import ReactDOM from "react-dom"
    import Hello from "hello"
    
    const container = document.getElementById('root')
    const root = ReactDOM.createRoot(container)
    root.render(React.createElement(Hello, { name: "taiju" }, null))
    </script>
    

Simple and nice. importmap is great!

BTW, Is ESM my company’s name? Oh, ES Modules, Sorry!