close
  • English
  • Module Federation

    This chapter introduces how to build Module Federation output in Rslib.

    Usage scenarios

    Module federation has some typical usage scenarios, including:

    • Allows independent applications (called "Micro-Frontend" in the Micro-Frontend architecture) to share modules without having to recompile the entire application.
    • Different teams work on different parts of the same application without having to recompile the entire application.
    • Dynamic code loading and sharing between applications at runtime.

    Module Federation can help you:

    • Reduce code duplication
    • Improve code maintainability
    • Reduce the overall size of the application
    • Improve application performance

    Quick start

    First install the Module Federation Rsbuild Plugin.

    npm
    yarn
    pnpm
    bun
    deno
    npm add @module-federation/rsbuild-plugin -D

    Then register the plugin in the rslib.config.ts file:

    rslib.config.ts
    import { 
    const pluginModuleFederation: (moduleFederationOptions: ModuleFederationOptions, rsbuildOptions?: RSBUILD_PLUGIN_OPTIONS) => RsbuildPlugin
    pluginModuleFederation
    } from '@module-federation/rsbuild-plugin';
    import {
    const pluginReact: (options?: PluginReactOptions) => RsbuildPlugin
    pluginReact
    } from '@rsbuild/plugin-react';
    import {
    function defineConfig(config: RslibConfig): RslibConfig (+3 overloads)

    This function helps you to autocomplete configuration types. It accepts a Rslib config object, or a function that returns a config.

    defineConfig
    } from '@rslib/core';
    export default
    function defineConfig(config: RslibConfig): RslibConfig (+3 overloads)

    This function helps you to autocomplete configuration types. It accepts a Rslib config object, or a function that returns a config.

    defineConfig
    ({
    RslibConfig.lib: LibConfig[]
    lib
    : [
    // ... other format {
    LibConfig.format?: Format | undefined

    Output format for the generated JavaScript files.

    @defaultValue'esm'@seehttps://rslib.rs/config/lib/format
    format
    : 'mf',
    LibConfig.output?: RslibOutputConfig | undefined

    Options for build outputs.

    @inheritdoc
    output
    : {
    OutputConfig.distPath?: string | DistPathConfig | undefined

    Set the directory of the output files. Rsbuild will emit files to the specified subdirectory according to the file type.

    • string: Set the root output directory to a specific path, equivalent to distPath.root.
    • object: Set the output directory for each file type.
    distPath
    : './dist/mf',
    // for production, add online assetPrefix here
    OutputConfig.assetPrefix?: LiteralUnion<"auto", string> | undefined

    When using CDN in the production, you can use this option to set the URL prefix of static assets, similar to the output.publicPath config of Rspack. auto means use a relative path based on file location.

    @defaultserver.base
    assetPrefix
    : 'http://localhost:3001/mf',
    }, // for Storybook to dev
    EnvironmentConfig.dev?: Pick<DevConfig, AllowedEnvironmentDevKeys> | undefined

    Options for local development.

    dev
    : {
    assetPrefix?: boolean | LiteralUnion<"auto", string> | undefined

    Set the URL prefix of static assets in development mode, similar to the output.publicPath config of Rspack.

    @defaultserver.base
    assetPrefix
    : 'http://localhost:3001/mf',
    },
    EnvironmentConfig.plugins?: RsbuildPlugins | undefined

    Configure Rsbuild plugins.

    plugins
    : [
    function pluginModuleFederation(moduleFederationOptions: ModuleFederationOptions, rsbuildOptions?: RSBUILD_PLUGIN_OPTIONS): RsbuildPlugin
    pluginModuleFederation
    (
    {
    ModuleFederationPluginOptions.name?: string | undefined

    The name of the container.

    name
    : 'rslib_provider',
    ModuleFederationPluginOptions.exposes?: Exposes | undefined

    Modules that should be exposed by this container. When provided, property name is used as public name, otherwise public name is automatically inferred from request.

    exposes
    : {
    // add expose here }, // can not add 'remote' here, because you may build 'esm' or 'cjs' assets in one build. // if you want the Rslib package use remote module, please see below.
    ModuleFederationPluginOptions.shared?: Shared | undefined

    Modules that should be shared in the share scope. When provided, property names are used to match requested modules in this compilation.

    shared
    : {
    react: {
        singleton: true;
    }
    react
    : {
    SharedConfig.singleton?: boolean | undefined

    Allow only a single version of the shared module in share scope (disabled by default).

    singleton
    : true,
    }, 'react-dom': {
    SharedConfig.singleton?: boolean | undefined

    Allow only a single version of the shared module in share scope (disabled by default).

    singleton
    : true,
    }, }, }, {}, ), ], }, ], // for Storybook to dev
    RsbuildConfig.server?: ServerConfig | undefined

    Options for the Rsbuild server. Mainly applies to local development and preview. Some options (e.g. publicDir, base) also affect production builds.

    server
    : {
    ServerConfig.port?: number | undefined

    Specify a port number for Rsbuild server to listen.

    @default3000
    port
    : 3001,
    },
    RslibConfig.output?: RslibOutputConfig | undefined

    Options for build outputs.

    @inheritdoc
    output
    : {
    RslibOutputConfig.target?: RsbuildTarget | undefined

    Setting the build target for Rsbuild.

    @override@default'node'
    target
    : 'web',
    },
    EnvironmentConfig.plugins?: RsbuildPlugins | undefined

    Configure Rsbuild plugins.

    plugins
    : [
    function pluginReact(options?: PluginReactOptions): RsbuildPlugin
    pluginReact
    ()],
    });

    In this way, we have completed the integration of Rslib Module as a producer. After the construction is completed, we can see that the mf directory has been added to the product, and consumers can directly consume this package.

    In the above example we added a new format: 'mf' , which will help you add an additional Module Federation product, while also configuring the format of cjs and esm , which does not conflict.

    However, if you want this Rslib Module to consume other producers at the same time, do not use the build configuration remote parameter, because in other formats, this may cause errors, please refer to the example below using the Module Federation runtime.

    Develop MF remote module

    Use host app

    Rslib support developing Module Federation Rslib project with a host application.

    1. Start rslib mf-dev command of library

    Adding the dev command to the package.json file:

    package.json
    {
      "scripts": {
        "dev": "rslib mf-dev"
      }
    }

    Then run the dev command can start the Module Federation development mode, enabling consumption by your host app, along with Hot Module Replacement (HMR).

    npm
    yarn
    pnpm
    bun
    deno
    npm run dev

    2. Start host app

    Set up the host app to consume the Rslib Module Federation library. Check out the @module-federation/rsbuild-plugin for more information.

    rsbuild.config.ts
    import { 
    const pluginModuleFederation: (moduleFederationOptions: ModuleFederationOptions, rsbuildOptions?: RSBUILD_PLUGIN_OPTIONS) => RsbuildPlugin
    pluginModuleFederation
    } from '@module-federation/rsbuild-plugin';
    import {
    function defineConfig(config: RsbuildConfig): RsbuildConfig (+3 overloads)

    This function helps you to autocomplete configuration types. It accepts a Rsbuild config object, or a function that returns a config.

    defineConfig
    } from '@rsbuild/core';
    import {
    const pluginReact: (options?: PluginReactOptions) => RsbuildPlugin
    pluginReact
    } from '@rsbuild/plugin-react';
    export default
    function defineConfig(config: RsbuildConfig): RsbuildConfig (+3 overloads)

    This function helps you to autocomplete configuration types. It accepts a Rsbuild config object, or a function that returns a config.

    defineConfig
    ({
    EnvironmentConfig.plugins?: RsbuildPlugins | undefined

    Configure Rsbuild plugins.

    plugins
    : [
    function pluginReact(options?: PluginReactOptions): RsbuildPlugin
    pluginReact
    (),
    function pluginModuleFederation(moduleFederationOptions: ModuleFederationOptions, rsbuildOptions?: RSBUILD_PLUGIN_OPTIONS): RsbuildPlugin
    pluginModuleFederation
    (
    {
    ModuleFederationPluginOptions.name?: string | undefined

    The name of the container.

    name
    : 'rsbuild_host',
    ModuleFederationPluginOptions.remotes?: Remotes | undefined

    Container locations and request scopes from which modules should be resolved and loaded at runtime. When provided, property name is used as request scope, otherwise request scope is automatically inferred from container location.

    remotes
    : {
    rslib: string
    rslib
    : 'rslib@http://localhost:3001/mf/mf-manifest.json',
    },
    ModuleFederationPluginOptions.shared?: Shared | undefined

    Modules that should be shared in the share scope. When provided, property names are used to match requested modules in this compilation.

    shared
    : {
    react: {
        singleton: true;
    }
    react
    : {
    SharedConfig.singleton?: boolean | undefined

    Allow only a single version of the shared module in share scope (disabled by default).

    singleton
    : true,
    }, 'react-dom': {
    SharedConfig.singleton?: boolean | undefined

    Allow only a single version of the shared module in share scope (disabled by default).

    singleton
    : true,
    }, }, // Enable this when the output of Rslib is build under 'production' mode, while the host app is 'development'. // Reference: https://rslib.rs/guide/advanced/module-federation#faqs
    ModuleFederationPluginOptions.shareStrategy?: SharedStrategy | undefined

    load shared strategy(defaults to 'version-first').

    shareStrategy
    : 'loaded-first',
    }, {}, ), ], });

    Then start the host app with rsbuild dev.

    Use Storybook

    Rslib support developing Module Federation Rslib project with Storybook.

    1. Start rslib mf-dev command of library

    Adding the dev command to the package.json file:

    package.json
    {
      "scripts": {
        "dev": "rslib mf-dev"
      }
    }

    Then run the dev command can start the Module Federation development mode, enabling consumption by Storybook, along with Hot Module Replacement (HMR).

    npm
    yarn
    pnpm
    bun
    deno
    npm run dev

    2. Set up Storybook configuration

    First, set up Storybook with the Rslib project. You can refer to the Storybook chapter to learn how to do this. In this chapter, we will use React as the framework for our example.

    1. Install the following Storybook addons to let Storybook work with Rslib Module Federation:

      npm
      yarn
      pnpm
      bun
      deno
      npm add storybook-addon-rslib @module-federation/storybook-addon -D
    2. Then set up the Storybook configuration file .storybook/main.ts, specify the stories and addons, and set the framework with corresponding framework integration.

      .storybook/main.ts
      import { dirname, join } from 'node:path';
      import type { StorybookConfig } from 'storybook-react-rsbuild';
      
      const getAbsolutePath = (value: string): string =>
        dirname(require.resolve(join(value, 'package.json')));
      
      const config: StorybookConfig = {
        stories: [
          '../stories/**/*.mdx',
          '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)',
        ],
        framework: {
          name: getAbsolutePath('storybook-react-rsbuild'),
          options: {},
        },
        addons: [
          {
            name: getAbsolutePath('storybook-addon-rslib'),
            options: {
              rslib: {
                include: ['**/stories/**'],
              },
            },
          },
          {
            name: '@module-federation/storybook-addon/preset',
            options: {
              // add your rslib module manifest here for storybook dev
              // we have set dev.assetPrefix and server.port to 3001 in rslib.config.ts above
              remotes: {
                'rslib-module':
                  // you can also add shared here for storybook app
                  // shared: {}
                  'rslib-module@http://localhost:3001/mf/mf-manifest.json',
              },
            },
          },
        ],
      };
      
      export default config;

    3. Writing stories with remote module

    Import components from remote module.

    stories/index.stories.tsx
    import React from 'react';
    // Load your remote module here, Storybook will act as the host app.
    import { Counter } from 'rslib-module';
    
    const Component = () => <Counter />;
    
    export default {
      title: 'App Component',
      component: Component,
    };
    
    export const Primary = {};

    4. Add Module Federation types and stories into tsconfig.json.

    tsconfig.json
    {
      "compilerOptions": {
        // ...
        "paths": {
          "*": ["./@mf-types/*"]
        }
      },
      "include": ["src/**/*", ".storybook/**/*", "stories/**/*"]
    }

    5. Start Storybook app

    There you go, start Storybook with npx storybook dev.

    Consume other Module Federation modules

    Because there are multiple formats in Rslib, if you configure the remote parameter to consume other modules during construction, it may not work properly in all formats. It is recommended to access through the Module Federation Runtime

    First install the runtime dependencies

    npm
    yarn
    pnpm
    bun
    deno
    npm add @module-federation/enhanced -D

    Then consume other Module Federation modules at runtime, for example

    import { init, loadRemote } from '@module-federation/enhanced/runtime';
    import { Suspense, createElement, lazy } from 'react';
    
    init({
      name: 'rslib_provider',
      remotes: [
        {
          name: 'mf_remote',
          entry: 'http://localhost:3002/mf-manifest.json',
        },
      ],
    });
    
    export const Counter: React.FC = () => {
      return (
        <div>
          <Suspense fallback={<div>loading</div>}>
            {createElement(
              lazy(
                () =>
                  loadRemote('mf_remote') as Promise<{
                    default: React.FC;
                  }>,
              ),
            )}
          </Suspense>
        </div>
      );
    };

    This ensures that modules can be loaded as expected in multiple formats.

    FAQs

    How to control the loading strategy of shared dependencies when the producer and consumer build patterns are different

    If the Rslib producer is built with build, this means that the process.env.NODE_ENV of the producer is production . If the consumer is started in dev mode at this time, due to the shared loading strategy of Module Federation being version-first by default, there may be problems loading into different modes of react and react-dom (e.g. react in development mode, react-dom in production mode). You can set up shareStrategy at the consumer to solve this problem, but make sure you fully understand this configuration

    pluginModuleFederation({
      // ...
      shareStrategy: 'loaded-first',
    }, {}),

    How to make module federated outputs generate export of ES modules

    If you want Rslib producers' module federated outputs to generate the export of ES Modules, you can additionally configure as follows:

    rslib.config.ts
    export default defineConfig({
      lib: [
        {
          format: 'mf',
          // ...
          tools: {
            rspack(config) {
              config.experiments = {
                outputModule: true,
              };
            },
          },
        },
      ],
    });

    Examples

    You can refer to the example projects in Rslib Module Federation Example.

    • mf-host: Rsbuild App Consumer
    • mf-react-component: Rslib Module, which is both a producer and a consumer, provides the module to the mf-host as a producer, and consumes the mf-remote
    • mf-remote: Rsbuild App Producer