Runtime Hooks

beforeInit

SyncWaterfallHook

Updates the corresponding init configuration before the MF instance is initialized.

  • type
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.

  • type
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.

  • type
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.

  • type
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.

  • type
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;
}

handlePreloadModule

SyncHook

Handles the preloading logic for remotes.

  • type
function handlePreloadModule(args: HandlePreloadModuleOptions): void

type HandlePreloadModuleOptions ={
  id: string;
  name: string;
  remoteSnapshot: ModuleInfo;
  preloadConfig: PreloadRemoteArgs;
}

errorLoadRemote

AsyncHook

Called if loading remotes fails, enabling custom error handling. Can return a custom fallback logic.

  • type
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
  • example
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;

        // Log error details safely
        if (error) {
          console.warn(`Failed to load remote ${id} at ${lifecycle}:`, error?.message || error);
        }

        // Handle different error types based on lifecycle
        switch (lifecycle) {
          case 'afterResolve':
            // Manifest loading failed - provide fallback manifest or alternative URL
            return {
              id: id || 'fallback',
              name: id || 'fallback',
              metaData: { /* fallback manifest */ },
              shared: [],
              remotes: [],
              exposes: []
            };

          case 'beforeRequest':
            // Request processing failed - can return modified args or void
            console.warn(`Request processing failed for ${id}`);
            return void 0;

          case 'onLoad':
            // Module loading failed - provide fallback component
            return () => ({
              __esModule: true,
              default: () => 'Fallback Component'
            });

          case 'beforeLoadShare':
            // Shared dependency loading failed - return fallback shared module
            console.warn(`Shared dependency loading failed for ${id}`);
            return () => ({
              __esModule: true,
              default: {}
            });

          default:
            // Unknown lifecycle - log and return void
            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.

  • type
async function beforeLoadShare(args: BeforeLoadShareOptions): Promise<BeforeLoadShareOptions>

type BeforeLoadShareOptions ={
  pkgName: string;
  shareInfo?: Shared;
  shared: Options['shared'];
  origin: ModuleFederation;
}

resolveShare

SyncWaterfallHook

Allows manual setting of the actual shared module to be used.

  • type
function resolveShare(args: ResolveShareOptions): ResolveShareOptions

type ResolveShareOptions ={
  shareScopeMap: ShareScopeMap;
  scope: string;
  pkgName: string;
  version: string;
  GlobalFederation: Federation;
  resolver: () => Shared | undefined;
}
  • example
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 { shareScopeMap, scope, pkgName, version, GlobalFederation } = args;

        if (
          pkgName !== 'react'
        ) {
          return args;
        }

        args.resolver = function () {
          shareScopeMap[scope][pkgName][version] = window.React; // replace local share scope manually with desired module
          return shareScopeMap[scope][pkgName][version];
        };
        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()]
});

window.React = ()=> 'Desired Shared';

mf.loadShare("react").then((reactFactory)=>{
  expect(reactFactory()).toEqual(window.React());
});

beforePreloadRemote

AsyncHook

Called before the preload handler executes any preload logic.

  • type
async function beforePreloadRemote(args: BeforePreloadRemoteOptions): BeforePreloadRemoteOptions

type BeforePreloadRemoteOptions ={
  preloadOps: Array<PreloadRemoteArgs>;
  options: Options;
  origin: ModuleFederation;
}

generatePreloadAssets

AsyncHook

Used to control the generation of resources that need to be preloaded.

  • type
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>;
}

createScript

SyncHook

Used to modify the script when loading resources.

  • type
function createScript(args: CreateScriptOptions): HTMLScriptElement | void

type CreateScriptOptions ={
  url: string;
}
  • example
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

  • Type
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'
      });
    },
  }
};

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

  • Type
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}
  • Example Delegate Modules
// 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"