Configuration

Applies to

Every option you can pass to initFederation — host entry, import-map implementation, logging, resolution modes and storage.

Everything you pass as the second argument to initFederation lives on this page. Options are grouped into five concerns — host entry, import-map implementation, logging, modes, and storage — and every option ships with a sensible default, so a bare initFederation(manifest) is already a valid call.

1. Host configuration

A host remote entry is a remoteEntry.json published by the host itself. Whenever an external appears both in the host entry and in a regular remote, the host version wins — making this the right escape hatch for locking a framework or design-system version globally.

type HostOptions = {
  hostRemoteEntry?:
    | string
    | false
    | {
        name?: string;
        url: string;
        cacheTag?: string;
        integrity?: string;
      };
  manifestIntegrity?: string;
};
OptionDefaultDescription
hostRemoteEntryfalseAdds a host remoteEntry.json that takes precedence during shared-version resolution. Can be a URL, the full object form, or false to disable.
hostRemoteEntry.cacheTagnoneOpaque string appended as a query parameter — the orchestrator treats a new cacheTag as a different file and refetches. Use this to bust caches after a host redeploy.
hostRemoteEntry.integritynoneSRI hash (sha256-…, sha384-…, sha512-…) verified against the response bytes of the host remoteEntry.json before it is parsed. See Security — Subresource Integrity.
manifestIntegritynoneSRI hash for the manifest URL passed as the first argument to initFederation. When set, the orchestrator hashes the response bytes and rejects with NFError on mismatch.

Example

import { initFederation } from "@softarc/native-federation-orchestrator";

initFederation("http://example.org/manifest.json", {
  manifestIntegrity: "sha384-…",
  hostRemoteEntry: {
    url: "./remoteEntry.json",
    cacheTag: "v1.2.3",
    integrity: "sha384-…",
  },
});

Per-remote pinning lives in the manifest itself — entries can be either the existing string form or a { url, integrity } object, and the two forms can coexist:

{
  "team/mfe1": "https://mfe1.example.org/remoteEntry.json",
  "team/mfe2": {
    "url": "https://mfe2.example.org/remoteEntry.json",
    "integrity": "sha384-…"
  }
}

See Security — Subresource Integrity for the full trust chain (manifest → remoteEntry.json → modules) and the supported hash algorithms.

2. Import-map implementation

The orchestrator commits a standard import map to the DOM and uses the browser's own import() to load modules. For older browsers — or whenever you need dynamic init — swap in es-module-shims.

type ImportMapOptions = {
  loadModuleFn?: (url: string) => Promise<unknown>;
  setImportMapFn?: (
    importMap: ImportMap,
    opts?: { override?: boolean },
  ) => Promise<ImportMap>;
  reloadBrowserFn?: () => void;
  trustedTypesPolicyName?: string | false;
};
OptionDefaultDescription
setImportMapFnreplaceInDOM('importmap')How to commit an import map — by default, replaces any existing <script type="importmap"> in the DOM.
loadModuleFnurl => import(url)How a module is actually imported. Override when you need the shim loader or custom instrumentation.
reloadBrowserFn() => window.location.reload()Called when the SSE dev feature detects a rebuilt remote. Override for custom reload UX.
trustedTypesPolicyName'nfo'Name of the Trusted Types policy that wraps import-map content and dynamic-import URLs in the default setImportMapFn and loadModuleFn. Pass false to opt out (e.g. when the host owns its own Trusted Types pipeline). No effect on browsers that do not support Trusted Types — the wrapper falls back to a transparent pass-through.

Two ready-made presets

import "es-module-shims";
import { initFederation } from "@softarc/native-federation-orchestrator";
import {
  useShimImportMap,
  useDefaultImportMap,
  replaceInDOM,
} from "@softarc/native-federation-orchestrator/options";

initFederation("http://example.org/manifest.json", {
  // Option 1 — native import maps (default)
  ...useDefaultImportMap(),

  // Option 2 — es-module-shims
  ...useShimImportMap({ shimMode: true }),

  // Option 3 — custom
  loadModuleFn: (url) => customImport(url),
  setImportMapFn: replaceInDOM("importmap"),

  // Option 4 — rename the Trusted Types policy to match a stricter CSP allowlist
  trustedTypesPolicyName: "my-app-nfo",
});

See Security — Trusted Types for the recommended CSP header and how the orchestrator interacts with a host-defined policy.

useShimImportMap is required for dynamic init — native import maps can be committed to the DOM only once, while es-module-shims accepts additional maps at runtime.

3. Logging

Diagnostics for both development and production. Ships with two built-in loggers and accepts any object that matches the Logger interface.

type LoggingOptions = {
  logger?: Logger;
  logLevel?: "debug" | "warn" | "error";
  sse?: boolean;
};

interface Logger {
  debug(step: number, msg: string, details?: unknown): void;
  warn(step: number, msg: string, details?: unknown): void;
  error(step: number, msg: string, details?: unknown): void;
}
OptionDefaultDescription
loggernoopLoggerWhere logs go. Use consoleLogger during development, or provide your own for Sentry / Bugsnag / etc.
logLevel'error'Level threshold. 'warn' emits warn+error; 'debug' emits everything.
ssefalseDev feature — listens to server-sent rebuild events from remotes and triggers reloadBrowserFn.

Example

import { initFederation } from "@softarc/native-federation-orchestrator";
import {
  noopLogger,
  consoleLogger,
} from "@softarc/native-federation-orchestrator/options";

initFederation("http://example.org/manifest.json", {
  logLevel: "debug",
  logger: consoleLogger, // or noopLogger, or a custom Logger
});

4. Modes — strictness & resolution profile

Mode options are the hyperparameters for the Version Resolver: how strict to be when something unexpected happens, and how aggressively to reuse cached state.

type ModeOptions = {
  strict?:
    | boolean
    | {
        strictRemoteEntry?: boolean;
        strictExternalCompatibility?: boolean;
        strictExternalSameVersionCompatibility?: boolean;
        strictExternalVersion?: boolean;
        strictImportMap?: boolean;
      };
  profile?: {
    latestSharedExternal?: boolean;
    skipInvalidExternalVersions?: boolean;
    overrideCachedRemotes?: "always" | "never" | "init-only";
    overrideCachedRemotesIfURLMatches?: boolean;
    cacheTag?: string;
  };
  feature?: {
    convertFlatSharedInfo?: boolean;
    useAutoExternalPooling?: boolean;
  };
};

Strictness

All flags default to false, which means "log and continue". Setting strict: true turns them all on at once.

OptionEffect
strictShortcut — sets every specific strict flag below to true.
strict.strictRemoteEntryThrows on malformed remoteEntry.json. When false, the broken remote is skipped and initialization continues.
strict.strictExternalCompatibilityThrows when two shared externals have incompatible version ranges. When false, the incompatible version is demoted to a scoped external with a warning.
strict.strictExternalSameVersionCompatibilityNiche edge case — throws when an already-cached shared version is re-submitted with a different requiredVersion range. Otherwise, the cached entry is preserved.
strict.strictExternalVersionThrows if a shared external's version is missing or not valid semver. When false, the external is instead coerced to the smallest version matching its requiredVersion range — unless profile.skipInvalidExternalVersions is on, in which case it is skipped. This flag takes precedence over profile.skipInvalidExternalVersions.
strict.strictImportMapThrows when the import-map builder encounters corrupt cache state.

Resolution profile

The profile controls how the resolver picks winners and whether it refreshes cached remotes.

OptionDefaultDescription
profile.latestSharedExternalfalseWhen true, always pick the highest version in the scope. When false (default), pick the version that minimizes extra scoped downloads.
profile.skipInvalidExternalVersionsfalseWhen true, an external whose version is missing or not valid semver is skipped (never stored) instead of being coerced to the smallest version of its requiredVersion range. Has no effect when strict.strictExternalVersion is set — that throws instead.
profile.overrideCachedRemotes'init-only'When to refetch a remote that already lives in cache — see below.
profile.overrideCachedRemotesIfURLMatchesfalseBy default, a cached remote is only overridden when its URL changed. Set this to true to force refetch even when the URL is identical.
profile.cacheTagnoneWhen set, appended as a ?cacheTag=<value> query parameter to every remote's remoteEntry.json request, letting you bust HTTP caches across all remotes at once. The host's own hostRemoteEntry.cacheTag takes precedence for the host entry when both are set.

overrideCachedRemotes values:

Features

Opt-in behaviors that change how shared externals are processed and stored. Both default to false.

OptionDefaultDescription
feature.convertFlatSharedInfofalseOpts into runtime densification of a remote's shared externals. Core v4.3.0 emits DenseSharedInfo (a per-package entries map covering primary and secondary entrypoints) natively, and those pass through unchanged. For older/flat remote builds that emit one flat SharedInfo per entrypoint, enabling this groups secondary entrypoints under their parent package (by npm scope) so they resolve as one shared external. See Version Resolver — Secondary entrypoints.
feature.useAutoExternalPoolingfalseWhen true, shared externals are grouped into pools by their npm scope (@framework/core, @framework/common → pool framework) so a coupled family resolves to one mutually-compatible version from a single remote build. Unscoped packages are not auto-pooled; a remote can also opt a specific external into a pool with a pool tag regardless of this flag. See Version Resolver — Dependency Pooling.

Two ready-made profiles

import { initFederation } from "@softarc/native-federation-orchestrator";
import {
  defaultProfile,
  cachingProfile,
} from "@softarc/native-federation-orchestrator/options";

// defaultProfile
// { latestSharedExternal: false, skipInvalidExternalVersions: false, overrideCachedRemotes: 'init-only', overrideCachedRemotesIfURLMatches: false }

// cachingProfile
// { latestSharedExternal: false, skipInvalidExternalVersions: false, overrideCachedRemotes: 'never',     overrideCachedRemotesIfURLMatches: false }

initFederation("http://example.org/manifest.json", {
  strict: true,
  profile: cachingProfile,
});

The caching profile is tempting but comes with a trade-off: newly deployed remote versions won't be picked up until the cache is explicitly cleared. Pair it with clearStorage: true on cache-busting events, or stick with defaultProfile when remotes change frequently.

5. Storage

The orchestrator keeps its internal caches (remote info, shared externals, scoped externals) inside a storage entry. The default is an in-memory map on globalThis, but any Storage-compatible backend works.

type StorageOptions = {
  storage?: StorageEntryCreator;
  clearStorage?: boolean;
  storageNamespace?: string;
};
OptionDefaultDescription
storageglobalThisStorageEntryHow the cache is persisted. Built-ins: globalThisStorageEntry, sessionStorageEntry, localStorageEntry. Custom implementations are supported.
clearStoragefalseWhen true, initFederation wipes the namespace before initializing — handy for one-shot cache busts after a deploy.
storageNamespace'__NATIVE_FEDERATION__'Namespace prefix for stored keys (e.g. __NATIVE_FEDERATION__.remotes). Change it to run multiple orchestrators on the same origin.

Example

import { initFederation } from "@softarc/native-federation-orchestrator";
import {
  globalThisStorageEntry,
  localStorageEntry,
  sessionStorageEntry,
} from "@softarc/native-federation-orchestrator/options";

initFederation("http://example.org/manifest.json", {
  clearStorage: true,
  storageNamespace: "__custom_namespace__",

  // Option 1 — in-memory (default)
  storage: globalThisStorageEntry,

  // Option 2 — persisted for the browser session
  storage: sessionStorageEntry,

  // Option 3 — persisted across browser restarts
  storage: localStorageEntry,
});

Most server-rendered hosts want sessionStorageEntry: it survives navigation-triggered full reloads (the whole point of caching here) but is automatically cleared when the tab closes, so a stale resolution can never live longer than the user's session.

Putting it together

import { initFederation } from "@softarc/native-federation-orchestrator";
import {
  consoleLogger,
  sessionStorageEntry,
  useShimImportMap,
  cachingProfile,
} from "@softarc/native-federation-orchestrator/options";

const { loadRemoteModule } = await initFederation(manifest, {
  // Host
  hostRemoteEntry: { url: "./host-remoteEntry.json", cacheTag: "2025-04-22" },

  // Import map
  ...useShimImportMap({ shimMode: true }),

  // Logging
  logLevel: "warn",
  logger: consoleLogger,

  // Modes
  strict: { strictRemoteEntry: true, strictExternalCompatibility: true },
  profile: cachingProfile,

  // Storage
  storage: sessionStorageEntry,
  storageNamespace: "__NATIVE_FEDERATION__",
  clearStorage: false,
});

See also