Runtime Hooks
beforeInit
SyncWaterfallHook
Updates the corresponding init configuration before the MF instance is initialized.
function beforeInit(args: BeforeInitOptions): BeforeInitOptions
type BeforeInitOptions ={
userOptions: UserOptions;
options: ModuleFederationRuntimeOptions;
origin: ModuleFederation;
shareInfo: ShareInfos;
}
interface ModuleFederationRuntimeOptions {
id?: string;
name: string;
version?: string;
remotes: Array<Remote>;
shared: ShareInfos;
plugins: Array<ModuleFederationRuntimePlugin>;
inBrowser: boolean;
}
init
SyncHook
Called after the MF instance is initialized.
function init(args: InitOptions): void
type InitOptions ={
options: ModuleFederationRuntimeOptions;
origin: ModuleFederation;
}
beforeRequest
AsyncWaterfallHook
Called before resolving the remote path, useful for updating something before lookup.
async function beforeRequest(args: BeforeRequestOptions): Promise<BeforeRequestOptions>
type BeforeRequestOptions ={
id: string;
options: ModuleFederationRuntimeOptions;
origin: ModuleFederation;
}
afterResolve
AsyncWaterfallHook
Called after resolving the remote path, allowing modification of the resolved content.
async function afterResolve(args: AfterResolveOptions): Promise<AfterResolveOptions>
type AfterResolveOptions ={
id: string;
pkgNameOrAlias: string;
expose: string;
remote: Remote;
options: ModuleFederationRuntimeOptions;
origin: ModuleFederation;
remoteInfo: RemoteInfo;
remoteSnapshot?: ModuleInfo;
}
onLoad
AsyncHook
Triggered once a federated module is loaded, allowing access and modification to the exports of the loaded file.
async function onLoad(args: OnLoadOptions): Promise<void>
type OnLoadOptions ={
id: string;
expose: string;
pkgNameOrAlias: string;
remote: Remote;
options: ModuleOptions;
origin: ModuleFederation;
exposeModule: any;
exposeModuleFactory: any;
moduleInstance: Module;
}
type ModuleOptions = {
remoteInfo: RemoteInfo;
host: ModuleFederation;
}
interface RemoteInfo {
name: string;
version?: string;
buildVersion?: string;
entry: string;
type: RemoteEntryType;
entryGlobalName: string;
shareScope: string;
}
beforeInitContainer
AsyncWaterfallHook
Triggered before the host calls remoteEntry.init(...). Useful for dynamically rewriting the shareScope / initScope used in this initialization and the remoteEntryInitOptions passed to the remote.
async function beforeInitContainer(args: BeforeInitContainerOptions): Promise<BeforeInitContainerOptions>
type BeforeInitContainerOptions ={
shareScope: ShareScopeMap[string];
initScope: InitScope;
remoteEntryInitOptions: RemoteEntryInitOptions;
remoteInfo: RemoteInfo;
origin: ModuleFederation;
}
initContainer
AsyncWaterfallHook
Triggered after remoteEntry.init(...) completes successfully. Useful for metrics, observability, or post-initialization customization.
async function initContainer(args: InitContainerOptions): Promise<InitContainerOptions>
type InitContainerOptions ={
shareScope: ShareScopeMap[string];
initScope: InitScope;
remoteEntryInitOptions: RemoteEntryInitOptions;
remoteInfo: RemoteInfo;
remoteEntryExports: RemoteEntryExports;
origin: ModuleFederation;
id?: string;
remoteSnapshot?: ModuleInfo;
}
handlePreloadModule
SyncHook
Handles the preloading logic for remotes.
function handlePreloadModule(args: HandlePreloadModuleOptions): void
type HandlePreloadModuleOptions ={
id: string;
name: string;
remote: Remote;
remoteSnapshot: ModuleInfo;
preloadConfig: PreloadRemoteArgs;
origin: ModuleFederation;
}
errorLoadRemote
AsyncHook
Called if loading remotes fails, enabling custom error handling. Can return a custom fallback logic.
async function errorLoadRemote(args: ErrorLoadRemoteOptions): Promise<void | unknown>
type ErrorLoadRemoteOptions ={
id: string;
error: unknown;
options?: any;
from: 'build' | 'runtime';
lifecycle: 'beforeRequest' | 'beforeLoadShare' | 'afterResolve' | 'onLoad';
origin: ModuleFederation;
}
The lifecycle parameter indicates the stage where the error occurred:
beforeRequest: Error during initial request processing
afterResolve: Error during manifest loading (most common for network failures)
onLoad: Error during module loading and execution
beforeLoadShare: Error during shared dependency loading
import { createInstance, loadRemote } from '@module-federation/enhanced/runtime'
import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime';
const fallbackPlugin: () => ModuleFederationRuntimePlugin =
function () {
return {
name: 'fallback-plugin',
errorLoadRemote(args) {
const { lifecycle, id, error } = args;
if (error) {
console.warn(`Failed to load remote ${id} at ${lifecycle}:`, error?.message || error);
}
switch (lifecycle) {
case 'afterResolve':
return {
id: id || 'fallback',
name: id || 'fallback',
metaData: { /* fallback manifest */ },
shared: [],
remotes: [],
exposes: []
};
case 'beforeRequest':
console.warn(`Request processing failed for ${id}`);
return void 0;
case 'onLoad':
return () => ({
__esModule: true,
default: () => 'Fallback Component'
});
case 'beforeLoadShare':
console.warn(`Shared dependency loading failed for ${id}`);
return () => ({
__esModule: true,
default: {}
});
default:
console.warn(`Unknown lifecycle ${lifecycle} for ${id}`);
return void 0;
}
},
};
};
const mf = createInstance({
name: 'mf_host',
remotes: [
{
name: "remote",
alias: "app1",
entry: "http://localhost:2001/mf-manifest.json"
}
],
plugins: [fallbackPlugin()]
});
mf.loadRemote('app1/un-existed-module').then(mod=>{
expect(mod).toEqual('fallback');
});
beforeLoadShare
AsyncWaterfallHook
Called before loading shared, can be used to modify the corresponding shared configuration.
async function beforeLoadShare(args: BeforeLoadShareOptions): Promise<BeforeLoadShareOptions>
type BeforeLoadShareOptions ={
pkgName: string;
shareInfo?: Shared;
shared: Options['shared'];
origin: ModuleFederation;
}
initContainerShareScopeMap
SyncWaterfallHook
Triggered when the host aligns a remote’s share-scope mapping. Useful for redirecting one scope to another (for example, aliasing scope1 to default so they share the same pool).
function initContainerShareScopeMap(args: InitContainerShareScopeMapOptions): InitContainerShareScopeMapOptions
type InitContainerShareScopeMapOptions ={
shareScope: ShareScopeMap[string];
options: Options;
origin: ModuleFederation;
scopeName: string;
hostShareScopeMap?: ShareScopeMap;
}
resolveShare
SyncWaterfallHook
Allows overriding the final shared module selection result.
resolveShare runs after the runtime has already picked the candidate scope and version. At this point, changing fields like scope or version on args does not change the final result by itself. To switch to a different shared module, replace args.resolver and return the shared record you want to use.
function resolveShare(args: ResolveShareOptions): ResolveShareOptions
type ResolveShareOptions ={
shareScopeMap: ShareScopeMap;
scope: string;
pkgName: string;
version: string;
shareInfo: Shared;
GlobalFederation: Federation;
resolver: () => {
shared: Shared;
useTreesShaking: boolean;
} | undefined;
}
import { createInstance, loadRemote } from '@module-federation/enhanced/runtime'
import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime';
const customSharedPlugin: () => ModuleFederationRuntimePlugin =
function () {
return {
name: 'custom-shared-plugin',
resolveShare(args) {
const { pkgName, shareScopeMap } = args;
if (pkgName !== 'react') {
return args;
}
const fallbackShared = shareScopeMap.default?.react?.['17.0.0'];
if (!fallbackShared) {
return args;
}
args.resolver = function () {
return {
shared: fallbackShared,
useTreesShaking: false,
};
};
return args;
},
};
};
const mf = createInstance({
name: 'mf_host',
shared: {
react: {
version: '17.0.0',
scope: 'default',
lib: () => React,
shareConfig: {
singleton: true,
requiredVersion: '^17.0.0',
},
},
},
plugins: [customSharedPlugin()]
});
mf.loadShare('react').then((reactFactory) => {
expect(reactFactory()).toEqual(React);
});
beforePreloadRemote
AsyncHook
Called before the preload handler executes any preload logic.
async function beforePreloadRemote(args: BeforePreloadRemoteOptions): Promise<void>
type BeforePreloadRemoteOptions ={
preloadOps: Array<PreloadRemoteArgs>;
options: Options;
origin: ModuleFederation;
}
generatePreloadAssets
AsyncHook
Used to control the generation of resources that need to be preloaded.
async function generatePreloadAssets(args: GeneratePreloadAssetsOptions): Promise<PreloadAssets>
type GeneratePreloadAssetsOptions ={
origin: ModuleFederation;
preloadOptions: PreloadOptions[number];
remote: Remote;
remoteInfo: RemoteInfo;
remoteSnapshot: ModuleInfo;
globalSnapshot: GlobalModuleInfo;
}
interface PreloadAssets {
cssAssets: Array<string>;
jsAssetsWithoutEntry: Array<string>;
entryAssets: Array<EntryAssets>;
}
loaderHook
loaderHook is used to intercept resource loading and module-factory resolution.
createScript
SyncHook
Used to modify the script when loading resources.
function createScript(args: CreateScriptOptions): CreateScriptHookReturn
type CreateScriptOptions ={
url: string;
attrs?: Record<string, any>;
}
import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime';
const changeScriptAttributePlugin: () => ModuleFederationRuntimePlugin =
function () {
return {
name: 'change-script-attribute',
createScript({ url }) {
if (url === 'http://localhost:3001/remoteEntry.js') {
let script = document.createElement('script');
script.src = url;
script.setAttribute('loader-hooks', 'isTrue');
script.setAttribute('crossorigin', 'anonymous');
return script;
}
}
};
};
fetch
The fetch function allows customizing the request that fetches the manifest JSON. A successful Response must yield a valid JSON.
AsyncHook
function fetch(manifestUrl: string, requestInit: RequestInit): Promise<Response> | void | false;
- Example for including the credentials when fetching the manifest JSON:
// fetch-manifest-with-credentials-plugin.ts
import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';
export default function (): FederationRuntimePlugin {
return {
name: 'fetch-manifest-with-credentials-plugin',
fetch(manifestUrl, requestInit) {
return fetch(manifestUrl, {
...requestInit,
credentials: 'include'
});
},
}
};
createLink
SyncHook
Used to customize the link element created for preload/style loading.
function createLink(args: CreateLinkOptions): HTMLLinkElement | void
type CreateLinkOptions ={
url: string;
attrs?: Record<string, any>;
}
loadEntryError
AsyncHook
Triggered when loading remoteEntry fails (typically script loading errors). Useful for retries or custom fallback strategies.
async function loadEntryError(args: LoadEntryErrorOptions): Promise<Promise<RemoteEntryExports | undefined> | undefined>
type LoadEntryErrorOptions ={
getRemoteEntry: typeof getRemoteEntry;
origin: ModuleFederation;
remoteInfo: RemoteInfo;
remoteEntryExports?: RemoteEntryExports;
globalLoading: Record<string, Promise<void | RemoteEntryExports> | undefined>;
uniqueKey: string;
}
getModuleFactory
AsyncHook
Triggered before calling remoteEntry.get(expose), allowing custom module-factory resolution.
async function getModuleFactory(args: GetModuleFactoryOptions): Promise<(() => Promise<Module>) | undefined>
type GetModuleFactoryOptions ={
remoteEntryExports: RemoteEntryExports;
expose: string;
moduleInfo: RemoteInfo;
}
loadEntry
The loadEntry function allows for full customization of remotes, enabling you to extend and create new remote types. The following two simple examples demonstrate loading JSON data and module delegation.
asyncHook
function loadEntry(args: LoadEntryOptions): RemoteEntryExports | void;
type LoadEntryOptions = {
createScriptHook: SyncHook,
remoteEntryExports?: RemoteEntryExports,
remoteInfo: RemoteInfo
};
interface RemoteInfo {
name: string;
version?: string;
buildVersion?: string;
entry: string;
type: RemoteEntryType;
entryGlobalName: string;
shareScope: string;
}
export type RemoteEntryExports = {
get: (id: string) => () => Promise<Module>;
init: (
shareScope: ShareScopeMap[string],
initScope?: InitScope,
remoteEntryInitOPtions?: RemoteEntryInitOptions,
) => void | Promise<void>;
};
- Example Loading JSON Data
// load-json-data-plugin.ts
import { init } from '@module-federation/enhanced/runtime';
import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';
const changeScriptAttributePlugin: () => FederationRuntimePlugin = function () {
return {
name: 'load-json-data-plugin',
loadEntry({ remoteInfo }) {
if (remoteInfo.jsonA === "jsonA") {
return {
init(shareScope, initScope, remoteEntryInitOPtions) {},
async get(path) {
const json = await fetch(remoteInfo.entry + ".json").then(res => res.json())
return () => ({
path,
json
})
}
}
}
},
};
};
// module-federation-config
{
remotes: {
jsonA: "jsonA@https://cdn.jsdelivr.net/npm/@module-federation/runtime/package"
}
}
// src/bootstrap.js
import jsonA from "jsonA"
jsonA // {...json data}
// delegate-modules-plugin.ts
import { init } from '@module-federation/enhanced/runtime';
import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';
const changeScriptAttributePlugin: () => FederationRuntimePlugin = function () {
return {
name: 'delegate-modules-plugin',
loadEntry({ remoteInfo }) {
if (remoteInfo.name === "delegateModulesA") {
return {
init(shareScope, initScope, remoteEntryInitOPtions) {},
async get(path) {
path = path.replace("./", "")
const {[path]: factory} = await import("./delegateModulesA.js")
const result = await factory()
return () => result
}
}
}
},
};
};
// ./src/delegateModulesA.js
export async function test1() {
return new Promise(resolve => {
setTimeout(() => {
resolve("test1 value")
}, 3000)
})
}
export async function test2() {
return new Promise(resolve => {
setTimeout(() => {
resolve("test2 value")
}, 3000)
})
}
// module-federation-config
{
remotes: {
delegateModulesA: "delegateModulesA@https://delegateModulesA.js"
}
}
// src/bootstrap.js
import test1 from "delegateModulesA/test1"
import test2 from "delegateModulesA/test2"
test1 // "test1 value"
test2 // "test2 value"
bridgeHook
bridgeHook is defined in runtime-core/src/core.ts and is used to extend context across bridge render/destroy stages (for example, React/Vue bridge).
beforeBridgeRender
SyncHook
Triggered before bridge rendering. You can return an object to augment render arguments (for example, extraProps).
function beforeBridgeRender(args: Record<string, any>): void | Record<string, any>
afterBridgeRender
SyncHook
Triggered after bridge rendering.
function afterBridgeRender(args: Record<string, any>): void | Record<string, any>
beforeBridgeDestroy
SyncHook
Triggered before bridge teardown.
function beforeBridgeDestroy(args: Record<string, any>): void | Record<string, any>
afterBridgeDestroy
SyncHook
Triggered after bridge teardown.
function afterBridgeDestroy(args: Record<string, any>): void | Record<string, any>