Skip to main content
This guide walks you through setting up a knitkit host page in the browser. You will inject an import map before any module code runs, register one or more remote manifests so knitkit can negotiate shared dependencies, and then dynamically load any module a remote exposes — all without a bundler plugin.

The bootstrap constraint

The browser applies an import map exactly once, before it evaluates the first <script type="module">. This means you must inject the map in a <script type="importmap"> block that appears before your application’s entry-point script. knitkit relies on this ordering: registerRemotes calls injectImportMap internally, which merges the negotiated entries into any existing import map on the page. You must call registerRemotes before any federated import runs so that the entries are present when the browser resolves them. The recommended pattern is a tiny bootstrap script that registers remotes, then dynamically imports the rest of your app:
Import maps are supported natively in Chrome 89+, Firefox 108+, and Safari 16.4+. The integrity field inside an import map (module-level SRI) requires Chrome 127+, Firefox 138+, or Safari 18.4+. For older browsers, es-module-shims is a documented opt-in fallback — it is never the default. Re-verify these version numbers against MDN at ship time.

Registering remotes

registerRemotes accepts an array of remote descriptors and an options object. It fetches each manifest, negotiates shared dependency versions across all participants, injects the resulting import map into the page, and stores the registrations for later loadRemote calls.
Each key in hostShared is the bare package name that appears in the import map. The fields are: You can also pass a pre-fetched manifest object instead of a URL string if you are managing fetch yourself:

Loading exposed modules

Once registerRemotes has returned, call loadRemote anywhere in your application. The specifier follows the pattern "<remoteName>/<exposeKey>", where exposeKey is the key from the remote’s knit.config.json exposes array (minus the leading ./).
loadRemote resolves the expose URL from the registered manifest, imports it via the native dynamic import(), and returns the module’s default export when one exists — or the full module namespace otherwise.
Run knitkit types sync on your host to generate typed declarations for every specifier you load. After syncing, loadRemote("checkout/CartWidget") is fully typed — no manual type assertions needed.

Multiple remotes

Pass more than one descriptor in the remotes array to register several remotes in one call. knitkit fetches all manifests in parallel and negotiates shared deps across all of them together.

Inspecting the result

registerRemotes returns a NegotiationResult that includes the full import map and a resolution report. Two helper functions give you access to this state later without holding the return value yourself.
You can also inspect the live import map in Chrome DevTools. Open the Sources panel, expand the Import maps node in the file tree, and you will see every entry knitkit injected — useful for verifying which URL won the version negotiation for each shared package.