React Bridge(for React v16-v19)

@module-federation/bridge-react provides bridge utility functions for React applications:

  • createBridgeComponent: Used for exporting application-level modules, suitable for producers to wrap modules exported as application types
  • createRemoteComponent: Used for loading application-level modules, suitable for consumers to load modules as application types

React Version Compatibility

@module-federation/bridge-react supports multiple major React versions, ensuring seamless integration between applications using different React versions:

  • React 16/17 (Legacy): Fully supported, using traditional ReactDOM.render and ReactDOM.unmountComponentAtNode APIs
  • React 18: Fully supported, using the new createRoot and hydrateRoot APIs
  • React 19: Fully supported, using createRoot and hydrateRoot APIs, but with legacy render methods completely removed

Version-Specific Imports

You can choose specific import paths based on the React version used in your project:

// Auto-detect React version (recommended for most scenarios)
import { createBridgeComponent } from '@module-federation/bridge-react';

// Explicitly specify React 16/17 version (Legacy mode)
import { createBridgeComponent } from '@module-federation/bridge-react/legacy';

// Explicitly specify React 18 version
import { createBridgeComponent } from '@module-federation/bridge-react/v18';

// Explicitly specify React 19 version
import { createBridgeComponent } from '@module-federation/bridge-react/v19';

This multi-version compatibility allows different teams to independently choose and upgrade their React versions while maintaining stable Module Federation integration.

View Demo

Installation

npm
yarn
pnpm
npm install @module-federation/bridge-react@latest

Examples

Exporting Application Type Modules

DANGER

Note: After using @module-federation/bridge-react, you cannot set react-router-dom as shared, otherwise the build tool will throw an exception. This is because @module-federation/bridge-react implements route control by proxying react-router-dom to ensure that inter-application routing works correctly.

In the producer project, assuming we need to export the application as an application type module using @module-federation/bridge-react, with App.tsx as the application entry point.

  • Step 1: First, create a new file export-app.tsx, which will be the file exported as an application type module. We need to use createBridgeComponent to wrap the root component of the application.
// ./src/export-app.tsx
import App from './src/App.tsx';
import { createBridgeComponent } from '@module-federation/bridge-react';

export default createBridgeComponent({
  rootComponent: App
});
  • Step 2: In the rsbuild.config.ts configuration file, we need to export export-app.tsx as an application type module
// rsbuild.config.ts
import { pluginModuleFederation } from '@module-federation/rsbuild-plugin';

export default defineConfig({
  plugins: [
    pluginReact(),
    pluginModuleFederation({
      name: 'remote1',
      exposes: {
        './export-app': './src/export-app.tsx',
      },
      shared: ['react', 'react-dom'],
    }),
  ],
});

At this point, we have completed the export of the application type module.

INFO

Why do application type modules need to be wrapped with createBridgeComponent? There are three main reasons:

  1. Support for cross-framework rendering. Components wrapped with createBridgeComponent will conform to the loading protocol of the application type consumer, making cross-framework rendering possible.
  2. Automatic injection of basename. Components wrapped with createBridgeComponent will automatically inject basename, ensuring that the producer application works correctly under the consumer project.
  3. Wrapping ErrorBoundary. Components wrapped with createBridgeComponent will be wrapped with ErrorBoundary to ensure that fallback logic is automatically entered when remote loading fails or rendering errors occur.

Loading Application Type Modules

Host

  • Step 1: In the rsbuild.config.ts configuration, we need to register remote modules, which is no different from other Module Federation configurations.
// rsbuild.config.ts
export default defineConfig({
  plugins: [
    pluginReact(),
    pluginModuleFederation({
      name: 'host',
      remotes: {
        remote1: 'remote1@http://localhost:2001/mf-manifest.json',
      },
    }),
  ],
});
  • Step 2: In the host application, we need to use createRemoteComponent to load the remote application.
// ./src/App.tsx
import { createRemoteComponent } from '@module-federation/bridge-react';
import { loadRemote } from '@module-federation/runtime';

// Define FallbackError component
const FallbackErrorComp = ({ error }: { error: Error }) => {
  return (
    <div>
      <h1>Error loading remote component</h1>
      <p>{error.message}</p>
      <button onClick={() => window.location.reload()}>
        resetErrorBoundary(try again)
      </button>
    </div>
  );
};

// Define FallbackLoading component
const FallbackComp = <div data-test-id="loading">loading...</div>;

// Use createRemoteComponent to create remote component
const Remote1App = createRemoteComponent({
  // loader is used to load remote modules, e.g.: loadRemote('remote1/export-app'), import('remote1/export-app')
  loader: () => loadRemote('remote1/export-app'),
  // fallback is used for displaying components when remote module loading fails
  fallback: FallbackErrorComp,
  // loading is used for displaying components when loading remote modules
  loading: FallbackComp,
});

const App = () => {
  return (
    <BrowserRouter basename="/">
      <Routes>
        <Route path="/" Component={Home} />
        <Route
          path="/remote1/*"
          // Use Remote1App component, will be lazy loaded
          Component={() => (
            <Remote1App
              // Set basename for remote application
              basename="/remote1"
              // Other properties will be passed to remote component
              props1={'props_value'}
              props2={'another_props_value'}
            />
          )}
        />
      </Routes>
    </BrowserRouter>
  );
};
INFO
  1. The remote module exported by createRemoteComponent will automatically use the react-bridge loading protocol to load the module, making cross-framework rendering of applications possible.

  2. Additionally, createRemoteComponent will automatically handle module loading, module destruction, error handling, loading, routing, and other logic, allowing developers to focus solely on how to use the remote component.

API Reference

createBridgeComponent

createBridgeComponent is used to export remote React components.

/**
 * Create a bridge component for remote loading
 * @param bridgeInfo - Bridge component configuration information
 * @returns Returns a function that provides render and destroy methods
 */
function createBridgeComponent<T>(
  bridgeInfo: ProviderFnParams<T>
): () => {
  render(info: RenderFnParams): Promise<void>;
  destroy(info: DestroyParams): Promise<void>;
};

/**
 * Bridge component configuration information
 */
interface ProviderFnParams<T> {
  /** Root component to be remotely loaded */
  rootComponent: React.ComponentType<T>;
  
  /** 
   * Custom render function for custom rendering logic
   * @param App - React element
   * @param id - DOM element or string ID
   * @returns React root element or Promise
   */
  render?: (
    App: React.ReactElement,
    id?: HTMLElement | string,
  ) => RootType | Promise<RootType>;
  
  /** 
   * Custom createRoot function for React 18 and above
   * @param container - DOM container
   * @param options - CreateRoot options
   * @returns React root node
   */
  createRoot?: (
    container: Element | DocumentFragment,
    options?: CreateRootOptions,
  ) => Root;
  
  /**
   * Default options for createRoot in React 18 and 19
   * These options will be used when creating a root unless overridden by rootOptions in render params
   * @example
   * {
   *   identifierPrefix: 'app-',
   *   onRecoverableError: (err) => console.error(err)
   * }
   */
  defaultRootOptions?: CreateRootOptions;
}

createRemoteComponent

createRemoteComponent is used to load remote React components.

/**
 * Create remote React component
 * @param options - Remote component configuration options
 * @returns Returns a React component that can receive props and render remote component
 */
function createRemoteComponent<T, E extends keyof T = 'default'>(
  options: RemoteComponentParams<T, E>
): React.ForwardRefExoticComponent<
  React.PropsWithoutRef<RemoteComponentProps> & React.RefAttributes<HTMLDivElement>
>;

/**
 * Remote component configuration parameters
 */
interface RemoteComponentParams<
  T = Record<string, unknown>,
  E extends keyof T = keyof T
> {
  /** 
   * Function to load remote module
   * Example: () => loadRemote('remote1/export-app') or () => import('remote1/export-app')
   */
  loader: () => Promise<T>;
  
  /** Component displayed when loading remote module */
  loading: React.ReactNode;
  
  /** Error component displayed when loading or rendering remote module fails */
  fallback: React.ComponentType<{ error: Error }>;
  
  /** 
   * Specify module export name
   * Default is 'default'
   */
  export?: E;
}

/**
 * Remote component properties
 */
interface RemoteComponentProps<T = Record<string, unknown>> {
  /** Properties passed to remote component */
  props?: T;

  /** 
   * Memory route configuration, used to control child application routing as memoryRouter
   * Will not directly display URL in browser address bar
   */
  memoryRoute?: { entryPath: string };
  
  /** Base path name */
  basename?: string;
  
  /** Style */
  style?: React.CSSProperties;
  
  /** Class name */
  className?: string;
}

Usage Examples

Using export to specify module export

// remote
export const provider = createBridgeComponent({
  rootComponent: App
});

// host
const Remote1App = createRemoteComponent({
  loader: () => loadRemote('remote1/export-app'),
  export: 'provider', // Specify to use provider export
  fallback: FallbackErrorComp,
  loading: FallbackComp,
});

Using memoryRoute to control routing

function App() {
  return (
    <BrowserRouter basename="/">
      <Routes>
        <Route
          path="/remote1/*"
          Component={() => (
            <Remote1App
              className={styles.remote1}
              style={{ color: 'red' }}
              // Use memoryRoute to control child application routing as memoryRouter
              // Will not directly display URL in browser address bar
              memoryRoute={{ entryPath: '/detail' }}
              // Other properties will be passed to remote component
              props1={'props_value'}
              props2={'another_props_value'}
              ref={ref}
            />
          )}
        />
      </Routes>
    </BrowserRouter>
  );
}

Configuring createRoot Options for React 18 and 19

For React 18 and 19, @module-federation/bridge-react supports passing options to the createRoot method, allowing you to customize the behavior of your root component:

Setting Default Options When Creating a Bridge Component

import { createBridgeComponent } from '@module-federation/bridge-react';

export default createBridgeComponent({
  rootComponent: App,
  // Set default createRoot options for all instances using this component
  defaultRootOptions: {
    identifierPrefix: 'my-app-',
    onRecoverableError: (error) => {
      console.error('Recoverable rendering error:', error);
    }
  }
});

Setting Options at Render Time

// In the consumer application
const RemoteApp = createRemoteComponent({
  url: 'http://localhost:3001/remoteEntry.js',
  scope: 'remote',
  module: './App',
});

// Pass rootOptions when rendering
<RemoteApp 
  props={{ message: 'Hello' }} 
  rootOptions={{
    identifierPrefix: 'instance-',
    onRecoverableError: (error) => {
      console.error('Recoverable error for this instance:', error);
    }
  }}
/>

Options passed at render time will override the default options set when creating the component.