ESM View

Modular esm-views are built with the same Webpack or esbuild configuration and support the same functionalities as the app type, with the following essential differences:

  1. They are compiled as ES Modules

  2. All their external imports are rewritten to point to an ESM CDN.

  3. They expect their entry-point (src/index.tsx) to not render to the DOM, but to export something, typically a React Component.

The esm-view type is used for creating micro-frontends. They typically expect their entry-point exports to be dynamically imported at runtime by some other micro-frontend, but can be also served standalone exactly as an app, provided that they export a React component that can be rendered to the DOM.

Since the micro-frontend pattern allows different teams to build and serve their applications independently and compose them at runtime (parallelizing otherwise expensive build operations), third-party dependencies need to be de-duplicated across micro-frontends; for this reason, external dependencies are not bundled, but they are rewritten to a configurable ESM CDN that serves them as ES modules on the fly.

Supported features

ESM views support a subset of Create React App features that make sense in a monorepo context for a type of application that can be loaded at runtime. See this page for a detailed list of supported features.

Custom index.html

By deafult, the index.html that loads the ESM view in standalone mode is created automatically by Modular. If you want to specify your own index file, just create your public/index.html file in your ESM view workspace directory. The custom index (and the public folder) works in the same way as Create React App does.

Build

To build your ESM View for deployment, run:

modular build my-esm-view-name

The resulting output is an optimized site that can be served statically or imported at run-time using dynamic import. All code (files in src plus external dependencies required in the code) is bundled in a single blob of code that can be split in different files, except for external dependencies that are re-written to an ESM CDN (this behaviour is configurable).

Dynamically loading vs statically serving ESM Views

The resulting output of an ESM View contains an index.html file, which is generated from the user-specified public/index.html or is synthetically generated from a simple internal template if that file is missing. Either way, the resulting index.html links to a synthetically-generated _trampoline.js module, which allows an ESM View to be served like a static website. The JS and CSS bundles are hashed and linked in the generated package.json, that the loading code can fetch and examine before importing the esm-view at runtime. For example, this is the stucture of a built ESM View on the filesystem:

<modular-root>/dist/my-esm-view
├── asset-manifest.json
├── index.html
├── package.json
└── static
   ├── css
   |  ├── main.1915b736.css
   |  └── main.1915b736.css.map
   └── js
      ├── _trampoline.js
      ├── main.bf0399f0.js
      └── main.bf0399f0.js.map

where /static/js/_trampoline (and, possibly, index.html) is generated to statically serve the ESM View. It is also possible to load the ESM view dynamically, by discovering the hashed JS (/static/js/main.bf0399f0.js) and CSS (/static/css/main.1915b736.css) entrypoints from the generated /package.json:

{
  "name": "my-esm-view",
  "version": "1.0.0",
  "modular": {
    "type": "esm-view"
  },
  "dependencies": {
    "react": "^18.2.0"
  },
  "bundledDependencies": [],
  "module": "/static/js/main.bf0399f0.js",
  "style": "/static/css/main.1915b736.css"
}

and importing them at run-time (assuming they are served from baseUrl):

const { default: LoadedView } = await import(
  /* webpackIgnore: true */ `${baseUrl}/static/js/main.bf0399f0.js`
);
// Render LoadedView with React
const node = document.head;
const url = `${baseUrl}/static/css/main.1915b736.css`;
// Make sure that the CSS is not already loaded
if (!node.querySelector(`link[href="${url}"]`)) {
  node.insertAdjacentHTML(
    'beforeend',
    `<link rel='stylesheet' href='${url}' />`,
  );
}

Start

To run your ESM View locally on a development server, run start:

modular start my-esm-view-name

This starts a developer server on port 3000, serving the ESM View with an additional runtime layer that provides a trampoline module to load the generated files, plus developer experience functionalities like hot reloading and on-screen error overlay. Please make sure that your ESM View entry-point exports a React component as default for this to work.

Entry-point

ESM-Views need an entry-point file located at src/index.tsx, which by convention exports a React component as default for the modular start command to work.

Template

ESM Views are generated by modular add using the modular-template-esm-view template.

ESM View Reference

For an in-depth reference of how ESM Views work and can be customized, see this section.

Usage with <RemoteView />

The <RemoteView /> component is designed for consuming ESM Views in a portal-like application. It aims to make implementing the micro-frontend pattern easier. View the <RemoteView /> docs for more information.