> ## Documentation Index
> Fetch the complete documentation index at: https://knitkit.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# How knitkit Works: Remotes, Manifests, and Import Maps

> Understand remotes, hosts, manifests, shared dependencies, and import-map negotiation — the building blocks of knitkit module federation.

knitkit federation is built from a small set of composable ideas: a **host** that consumes federated modules, **remotes** that publish them, a **manifest** that describes what a remote offers, an **import map** that enforces singleton shared dependencies, and an **expose key** convention for addressing individual modules. Understanding these building blocks makes every other part of the system predictable.

## Host and Remote

A **remote** is any application or library that runs `knitkit build`. It produces a `knit.manifest.json` describing the modules it exposes and the shared dependencies it contributes, along with the built ESM artifacts for each. A remote is completely self-contained: serve its `dist/` directory from any static host and it is ready to be consumed.

A **host** is any application that calls `registerRemotes` at boot time. The host fetches one or more remote manifests, negotiates shared dependency versions across all participants (including its own declared shared dependencies), injects a single import map, and then loads individual exposed modules on demand with `loadRemote`.

The host and remote do not need to know about each other at build time. The only coupling is the manifest URL, which is provided at runtime. This means you can deploy a new version of a remote and have the host pick it up on the next page load without any changes to the host's build.

<Note>
  A single application can be both a host and a remote simultaneously — it can expose its own modules via a manifest while also loading modules from other remotes.
</Note>

## The Manifest

The manifest (`knit.manifest.json`) is the contract between a remote and its consumers. It is a versioned JSON document that the host fetches at boot to learn what the remote offers and what shared dependencies it expects.

Here is a representative manifest:

```json knit.manifest.json theme={null}
{
  "spec": "0.1",
  "name": "checkout",
  "exposes": {
    "./CartWidget": {
      "url": "./exposes/CartWidget.js",
      "types": "./types/CartWidget.d.ts"
    },
    "./CheckoutForm": {
      "url": "./exposes/CheckoutForm.js"
    }
  },
  "shared": {
    "react": {
      "version": "18.3.1",
      "requiredVersion": "^18.2.0",
      "singleton": true,
      "url": "./shared/react-18.3.1.js",
      "integrity": "sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9r7jQ..."
    }
  },
  "meta": {
    "buildTime": "2026-06-12T18:00:00Z",
    "framework": "react@18"
  }
}
```

| Field     | Required | Description                                                                                                    |
| --------- | -------- | -------------------------------------------------------------------------------------------------------------- |
| `spec`    | Yes      | The manifest spec version. Currently `"0.1"`. The runtime rejects manifests with an unrecognised spec version. |
| `name`    | Yes      | The remote's identifier, used as the prefix in `loadRemote`. Lowercase `[a-z][a-z0-9_-]*`.                     |
| `exposes` | Yes      | Map of expose key → expose declaration. May be an empty object.                                                |
| `shared`  | Yes      | Map of package name → shared declaration. May be an empty object.                                              |
| `meta`    | No       | Free-form metadata. Suggested keys: `buildTime`, `framework`.                                                  |

All relative URLs in the manifest (`url`, `types`) resolve against the manifest's own URL. You can host a remote on any path or CDN origin and nothing breaks.

## Shared Dependencies

Shared dependencies are packages that must resolve to a single instance across the host and all remotes. The canonical example is React: if a remote's hooks run against a different React instance than the host's renderer, React throws. The same constraint applies to any library that uses module-level singletons — stores, routers, context providers.

knitkit solves this with a direct property of import maps: **one import map entry maps to one URL, and one URL means one module instance in the browser cache**. There is no share scope, no runtime reference counting, and no configuration flag that can accidentally create a second copy.

Each participant in the federation — the host and every remote — declares its shared dependencies with three key fields:

| Field             | Description                                                                                                                                                                    |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `version`         | The exact version this participant contributes (i.e. what it built against).                                                                                                   |
| `requiredVersion` | A semver range this participant can accept. Supports `^`, `~`, `>=`, exact, and x-ranges like `1.x`.                                                                           |
| `singleton`       | If `true` (the default), a version conflict throws `KNIT_ERR_SINGLETON_CONFLICT`. If `false`, an incompatible version is loaded in isolation via an import map `scopes` entry. |

<Warning>
  Set `singleton: true` for any package that uses module-level state or React context. Setting `singleton: false` on React, for example, re-introduces the very problem knitkit is designed to eliminate.
</Warning>

## Import Map Negotiation

When you call `registerRemotes`, the runtime runs the following sequence:

1. **Fetch manifests** — each remote's `knit.manifest.json` is fetched in parallel. The response URL is used as the base for resolving relative `url` fields, so the manifest is portable.
2. **Negotiate shared deps** — `negotiateShared` merges the shared declarations from the host and all remotes. For each package, it selects the highest version that satisfies every participant's `requiredVersion`. If any participant declares `singleton: true` and no single version satisfies all ranges, the runtime throws `KNIT_ERR_SINGLETON_CONFLICT` immediately rather than silently loading a duplicate.
3. **Inject the import map** — `injectImportMap` writes a `<script type="importmap">` element into the document (or the equivalent structure in Node/edge environments) before any module import resolves through it. Every `import "react"` — from the host or from any remote — now resolves to the single winning URL.
4. **Register remotes** — the runtime stores each remote's manifest and base URL so `loadRemote` can look up expose URLs later.

<Tip>
  Call `getLastResult()` after `registerRemotes` to inspect the full `NegotiationResult`, including which version won for each package and from which URL. This is the programmatic equivalent of reading the import map in DevTools.
</Tip>

## Expose Keys

The `exposes` object in the manifest uses keys written with a leading `./`, following the Node.js package exports convention:

```json theme={null}
"exposes": {
  "./CartWidget": { "url": "./exposes/CartWidget.js" },
  "./CheckoutForm": { "url": "./exposes/CheckoutForm.js" }
}
```

When a consumer calls `loadRemote`, they combine the remote's `name` with the expose key, omitting the `./`:

```ts theme={null}
const CartWidget = await loadRemote("checkout/CartWidget");
//                                   ^^^^^^^^  ^^^^^^^^^^
//                                   name      expose key (without ./)
```

The runtime automatically normalises the key by prepending `./` before looking it up in the manifest. If the key is not found, `loadRemote` throws `KNIT_ERR_LOAD_FAILED` and lists the available keys in the error's suggestion field.

## SRI Integrity

The manifest's `integrity` field on each shared dependency and expose declaration holds a Subresource Integrity hash computed by `@knitkit/cli` at build time:

```json theme={null}
"react": {
  "version": "18.3.1",
  "url": "./shared/react-18.3.1.js",
  "integrity": "sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K..."
}
```

The `integrity` field is consumed in two ways:

* **In the browser**, the runtime places the hash in the import map's `integrity` key (available in Chrome 127+, Firefox 138+, Safari 18.4+). The browser enforces the hash on the module fetch, refusing to execute a tampered file.
* **In Node SSR**, `@knitkit/node`'s loader hooks verify the hash against the fetched bytes before the module is evaluated, providing the same protection server-side.

<Note>
  The `integrity` field is optional in the manifest schema. Remotes built without `@knitkit/cli` can omit it, but SRI verification will not be possible for those modules. For production deployments, always run `knitkit build` to get computed hashes.
</Note>
