Application-Level Modules

Modern.js provides runtime APIs to quickly export application-level Module Federation modules from your application.

We use the application created in Using Module Federation as an example to further explain how to import application-level modules.

Install Dependencies

Building on the existing application, we need to install the @module-federation/bridge-react dependency to use Bridge for loading application-level modules.

npm
yarn
pnpm
bun
npm add @module-federation/bridge-react

Exporting Modules from Producer

Unlike directly exporting component-level modules, we need to create a separate entry for application-level modules to be exported via Module Federation.

We create the src/export-App.tsx file:

NOTE

The filename can be arbitrary; Modern.js does not enforce a specific naming convention.

src/export-App.tsx
import '@modern-js/runtime/registry/main'; // This line must be included, it will import micro frontend runtime dependencies by default
import { render } from '@modern-js/runtime/browser';
import { createRoot } from '@modern-js/runtime/react';
import { createBridgeComponent } from '@module-federation/bridge-react';

const ModernRoot = createRoot();
export const provider = createBridgeComponent({
  rootComponent: ModernRoot,
  render: (Component, dom) => render(Component, dom),
});

export default provider;

This file will pass the root component of the main entry application to the Bridge API and render it to the specified node via Bridge's render function.

Next, we configure module-federation.config.ts to modify the export to src/export-App.tsx:

module-federation.config.ts
import { createModuleFederationConfig } from '@module-federation/modern-js';

export default createModuleFederationConfig({
  name: 'remote',
  filename: 'remoteEntry.js',
  exposes: {
    './app': './src/export-App.tsx',
  },
  shared: {
    react: { singleton: true },
    'react-dom': { singleton: true },
  },
});
INFO

createBridgeComponent is used to export application-level modules. Modern.js related APIs can be found at createRoot and render.

Using Modules in Consumer

We then modify the consumer configuration by removing the previously created src/routes/remote/page.tsx route file.

We want all routes that access /remote to enter the aforementioned application-level module, so we add src/routes/remote/$.tsx instead.

NOTE

If you are not familiar with the capabilities of $.tsx, please read Wildcard Routes.

src/routes/remote/$.tsx
import { createRemoteComponent } from '@module-federation/bridge-react';
import { loadRemote } from '@module-federation/modern-js/runtime';

const ErrorBoundary = (info?: { error: { message: string } }) => {
  return (
    <div>
      <h2>This is ErrorBoundary Component, Something went wrong:</h2>
      <pre style={{ color: 'red' }}>{info?.error.message}</pre>
    </div>
  );
};
const Loading = <div>loading...</div>;
const RemoteApp = createRemoteComponent({
  loader: () => loadRemote('remote/app'),
  fallback: ErrorBoundary,
  loading: Loading,
});

export default RemoteApp;
INFO

createRemoteComponent is used to load application-level modules.

Start the Application

Now, both the producer and consumer applications are set up. We can run modern dev locally to start both applications.

After startup, when the consumer application accesses the /remote route, it will enter the producer application. Accessing http://localhost:8080/remote will display a complete page of the producer's remote module in the browser.

You can create new route files in the producer application and add route navigation in the code. These functionalities will also work as expected.

You can refer to the example here: Modern.js & Module Federation Application-Level Modules.