Add and update documentation

Signed-off-by: David Festal <dfestal@redhat.com>
Assisted-by: Cursor
This commit is contained in:
David Festal
2025-11-06 15:44:22 +01:00
parent 1a656db530
commit 86bb4321a9
4 changed files with 181 additions and 0 deletions
@@ -0,0 +1,173 @@
---
id: module-federation
title: Module Federation
sidebar_label: Module Federation
description: Using Module Federation in Backstage
---
## Introduction
Module Federation is a feature that enables sharing code and dependencies between separately built JavaScript applications at runtime. In Backstage, module federation support allows you to:
- Build your frontend application as a **module federation host** that can load remote modules at runtime
- Package individual plugins or bundles of several plugins as **module federation remotes** that can be loaded dynamically
- Share dependencies efficiently between the host and remotes to avoid code duplication
This guide explains how to configure and build both module federation hosts and remotes in Backstage, and how to initialize module federation at runtime using the standard Module Federation Runtime API.
## Overview
### Module Federation Host vs Remotes
In module federation terminology:
- **Host**: The main frontend application that loads and consumes remote modules. In Backstage, this is your app package (typically `packages/app-next`).
- **Remote**: A separately built module that can be loaded by the host at runtime. In Backstage, these are typically plugin packages built as module federation remotes.
### Shared Dependencies
A critical aspect of module federation is **shared dependencies**. When a host loads remote modules, both need to share common dependencies (like React, React Router, Material-UI) to:
- Avoid loading the same dependency multiple times
- Ensure singleton dependencies (like React) only have one instance
- Enable context sharing between host and remotes
Backstage provides a list of default shared dependencies for common packages like React, React Router, and Material-UI. At build-time the `version` field is automatically resolved from your `package.json` files.
## Building the Module Federation Host
The module federation host is your main frontend application. By default, Backstage frontend applications include a default list of module federation shared dependencies.
When building and bundling the frontend application, the CLI automatically:
1. Resolves versions of the shared dependencies based on the monorepo dependencies
2. Adds an additional entrypoint to the frontend application bundle with the list of resolved runtime shared dependencies
## Building Module Federation Remotes
Plugin packages can be built as module federation remotes, allowing them to be loaded dynamically by a host application.
### Using the CLI
To build a plugin as a module federation remote, use the `--module-federation` option with the `package build` command:
```bash
cd plugins/my-plugin
yarn build --module-federation
```
### Build Output
When building a plugin as a module federation remote, the CLI:
1. Resolves versions of the shared dependencies based on the monorepo dependencies (done automatically by the Rspack/Webpack module federation plugin)
2. Produces the bundle assets in the `dist` folder, including:
- a `mf-manifest.json` file which contains the module federation manifest
- a `remoteEntry.js` file which is the main entrypoint for the remote module
## Runtime Usage
To use module federation in your Backstage app, you need to initialize the Module Federation Runtime with the shared dependencies configuration.
### Basic Usage
Here's how to initialize module federation in your app, and load remote modules:
```typescript title="packages/app/src/moduleFederation.ts"
import {
createInstance,
ModuleFederation,
} from '@module-federation/enhanced/runtime';
import { buildRuntimeSharedUserOption } from '@backstage/module-federation-common';
export async function initializeModuleFederation(): Promise<ModuleFederation> {
// Build the shared dependencies configuration
const { shared, errors } = await buildRuntimeSharedUserOption();
// Log any errors loading shared dependencies
if (errors.length > 0) {
for (const err of errors) {
console.error(err.message, err.cause);
}
}
// Initialize Module Federation Runtime
return createInstance({
name: 'app-next',
remotes: [
{
name: 'my_plugin',
entry: 'http://localhost:3001/mf-manifest.json',
},
],
shared,
});
}
export async function loadRemote(
instance: ModuleFederation,
name: string,
): Promise<any> {
return await instance.loadRemote<any>(name);
}
```
### Integration with Feature Loaders
Integration with frontend feature loaders is straightforward:
```typescript title="packages/app/src/App.tsx"
import { createInstance } from '@module-federation/enhanced/runtime';
import { buildRuntimeSharedUserOption } from '@backstage/module-federation-common';
import { createFrontendFeatureLoader } from '@backstage/frontend-plugin-api';
const moduleFederationInstance = createInstance({
name: 'app-next',
remotes: [],
});
const moduleFederationLoader = createFrontendFeatureLoader({
async loader() {
moduleFederationInstance.registerShared(
(await buildRuntimeSharedUserOption()).shared,
);
moduleFederationInstance.registerRemotes([
{
name: 'myFirstRemoteWith2ExposedModules',
entry:
'https://someCDN.org/myFirstRemoteWith2ExposedModules/mf-manifest.json',
},
{
name: 'mySecondRemote',
entry: 'https://someCDN.org/mySecondRemote/mf-manifest.json',
},
]);
const myFirstRemoteModule1 = await moduleFederationInstance.loadRemote<any>(
'myFirstRemoteWith2Exposes/module1',
);
const myFirstRemoteModule2 = await moduleFederationInstance.loadRemote<any>(
'myFirstRemoteWith2Exposes/module2',
);
const mySecondRemoteModule = await moduleFederationInstance.loadRemote<any>(
'mySecondRemote',
);
return [
myFirstRemoteModule1.default,
myFirstRemoteModule2.default,
mySecondRemoteModule.default,
];
},
});
const app = createApp({
features: [moduleFederationLoader],
});
export default app.createRoot();
```
The [`dynamicFrontendFeaturesLoader`](https://github.com/backstage/backstage/blob/master/packages/frontend-dynamic-feature-loader/src/loader.ts) provided in the [`@backstage/frontend-dynamic-feature-loader`](https://github.com/backstage/backstage/blob/master/packages/frontend-dynamic-feature-loader/README.md) package, which provides an integrated solution to load module federation remotes as dynamic frontend plugins, is a more complete example of a feature loader based on the module federation support.
## Default Shared Dependencies
Default shared dependencies are the same for both the host and remotes, and the list can be found in the [`@backstage/module-federation-common`](https://github.com/backstage/backstage/blob/master/packages/module-federation-common/src/defaults.ts) package.
+6
View File
@@ -270,6 +270,12 @@ that are exported from the package, leaving a much cleaner type definition file
and making sure that the type definitions are in sync with the generated
JavaScript.
### Building Module Federation Remotes
Frontend plugin packages can be built as module federation remotes, which allows them to be loaded dynamically at runtime by a module federation host (typically your main frontend app). To build a package as a module federation remote, use the `--module-federation` option with the `package build` command.
More details are given in the [Module Federation](../../frontend-system/building-apps/07-module-federation.md#building-module-federation-remotes) documentation.
## Bundling
The goal of the bundling process is to combine multiple packages together into a
+1
View File
@@ -200,6 +200,7 @@ Options:
--skip-build-dependencies Skip the automatic building of local dependencies. Applies to backend packages only.
--stats If bundle stats are available, write them to the output directory. Applies to app packages only.
--config <path> Config files to load instead of app-config.yaml. Applies to app packages only. (default: [])
--module-federation Build a package as a module federation remote. Applies to frontend plugin packages only.
```
## package lint
+1
View File
@@ -583,6 +583,7 @@ export default {
'frontend-system/building-apps/configuring-extensions',
'frontend-system/building-apps/built-in-extensions',
'frontend-system/building-apps/plugin-conversion',
'frontend-system/building-apps/module-federation',
'frontend-system/building-apps/migrating',
],
),