Skip to content

Import maps

Status: target state. The current gateway does not serve a centralized import map. Each package's browser bundle includes its dependencies bundled in. This page documents what import maps would do and why it's a known target.

Today: each package bundles its own

Every Matrix browser package's dist/browser/ is a self-contained bundle. If @open-matrix/chat and @open-matrix/director both depend on @open-matrix/core's browser modules, both bundles include their own copy.

Trade-offs:

  • Pro: Hermetic. A package's behavior doesn't change based on what other packages are loaded.
  • Pro: No coordination required. Packages can publish independently.
  • Con: Larger total payload when multiple Matrix apps load in the same page (the platform shell hosts several at once).
  • Con: Two copies of @open-matrix/core in the same browser context can break instanceof checks across packages.

Target state: shared modules via import maps

The browser's import maps spec lets a page declare:

html
<script type="importmap">
{
  "imports": {
    "@open-matrix/core": "/_shared/@open-matrix/core@1.0.2/dist/browser/index.js",
    "@open-matrix/chat-component": "/_shared/@open-matrix/chat-component@0.2.3/dist/browser/index.js"
  }
}
</script>

Subsequent import '@open-matrix/core' from any package's bundle resolves to the shared URL. The browser fetches each module once across the page.

For Matrix this would mean:

  1. The gateway serves a path like /_shared/<package>@<version>/... that reads from a Host-level shared module store.
  2. The platform shell injects an import map at page load referencing all shared dependencies.
  3. Package bundles are built to not include shared deps (a build flag), relying on the import map to resolve them.

Why it's not done yet

Three blockers:

  1. Cross-package version compatibility. With shared modules, two packages installed at incompatible @open-matrix/core versions can't coexist on one page. Today's bundle-everything approach hides this; an import map exposes it. The discovery-metadata requires/provides capability resolution (target state in the package catalog) is a prerequisite.
  2. Build pipeline coordination. Each package's build must know which modules to externalize. That requires a project-wide convention.
  3. Cache strategy. Shared modules want long cache lifetimes. The current gateway emits cache-control: no-cache on everything. Distinguishing immutable shared modules from per-package files requires a layered cache-control policy.

These are not technically hard but each requires alignment across many packages.

Workaround today

Packages that absolutely need to share state with another package use one of:

  • Same-package mounting. If two surfaces really need to share a class, mount them from the same package.
  • Bus-based coordination. Send messages over Matrix RR. Different package versions can interoperate by contract.
  • Globals. If a package defines a known global identifier (e.g. window.__matrix_runtime__), other packages can find it. This is fragile and discouraged.

Production reality

The biggest current cost of bundle-everything is the platform shell (@open-matrix/matrix-web) loading multiple webapp packages in nested iframes / module contexts. Each loads its own copy of core libraries.

For most use cases the cost is tolerable: each app is a single-digit MB. A shell loading 5 apps consumes maybe 30 MB of asset bytes — slow on first load, fine on warm cache.

When this becomes the bottleneck, import maps become priority.

See also

Source: Target-state planning in WORKSTREAMS/package-structure/. No import-map serving code exists in projects/matrix-3/packages/system-gateway-http/ today.