Modern.js

This plugin provides Module Federation supporting functions for Modern.js

Supports

  • modern.js ^2.56.1
  • Server-Side Rendering

We highly recommend referencing this application which takes advantage of the best capabilities: module-federation example

Quick Start

Installation

You can install the plugin with the following commands:

npm
yarn
pnpm
bun
npm add @module-federation/modern-js --save

Apply Plugin

Apply this plugin in plugins of modern.config.ts:

modern.config.ts
import { appTools, defineConfig } from '@modern-js/app-tools';
import { moduleFederationPlugin } from '@module-federation/modern-js';

export default defineConfig({
  dev: {
    port: 3005,
  },
  runtime: {
    router: true,
  },
  // moduleFederationPlugin is a plug-in for modern.js, which can make certain modifications to the build/runtime
  plugins: [appTools(), moduleFederationPlugin()],
});

Then create the module-federation.config.ts file and write the required configuration:

module-federation.config.ts
import { createModuleFederationConfig } from '@module-federation/modern-js';
export default createModuleFederationConfig({
  name: 'host',
  remotes: {
    remote: 'remote@http://localhost:3006/mf-manifest.json',
  },
  shared: {
    react: { singleton: true },
    'react-dom': { singleton: true },
  },
});

Type support

add /// <reference types='@module-federation/modern-js/types' /> in modern-app-env.d.ts to get type support.

modern-app-env.d.ts
+ /// <reference types='@module-federation/modern-js/types' />

Server-Side Rendering

INFO

For a better performance experience, Module Federation X Modern.js SSR only supports stream SSR.

There is no difference between using Module Federation in SSR scenarios and CSR scenarios. Developers can just keep following the original development behavior.

But for a better user experience, we provide supporting functions/components to help developers better use Module Federation.

createRemoteSSRComponent

  Type declaration
declare function createRemoteSSRComponent(
  props: CreateRemoteSSRComponentOptions
): (props: ComponentType) => React.JSX.Element;

type CreateRemoteSSRComponentOptions = {
  loader: () => Promise<T>;
  loading: React.ReactNode;
  fallback: ErrorBoundaryPropsWithComponent['FallbackComponent'];
  export?: E;
};

type ComponentType = T[E] extends (...args: any) => any
  ? Parameters<T[E]>[0] extends undefined
    ? Record<string, never>
    : Parameters<T[E]>[0]
  : Record<string, never>;

This function will also help inject the corresponding style tag/script while loading the component. This behavior can help avoid the CSS flickering problem caused by streaming rendering and accelerate the PID (first screen interactive time).

Example

import React, { FC, memo, useEffect } from 'react';
import { registerRemotes, createRemoteSSRComponent } from '@modern-js/runtime/mf';
// The remote declared in the build plug-in can be imported directly at the top level
import RemoteComp from 'remote/Image';


const RemoteSSRComponent = createRemoteSSRComponent({
  // The remote declared in the build plug-in can also be loaded using this function: loader: () => import('remote/Image'),
  loader: () => loadRemote('dynamic_remote/Image'),
  loading: <div>loading...</div>,
  fallback: ({ error }) => {
    if (error instanceof Error && error.message.includes('not exist')) {
      return <div>fallback - not existed id</div>;
    }
    return <div>fallback</div>;
  },
});

const Product: FC = () => {
  registerRemotes([
    {
      name: 'dynamic_remote',
      entry: 'http://localhost:3008/mf-manifest.json',
    },
  ]);

  const fallback = (err: Error) => {
    if (err.message.includes('does not exist in container')) {
      return <div>404</div>;
    }
    throw err;
  };

  return <>
    <RemoteSSRComponent />
    <RemoteComp />
  </>;
};
export default Product;

loading

  • Type:React.ReactNode
  • Required: Yes
  • Default value: undefined

Set module loading status.

fallback

  • Type:((err: Error) => React.ReactElement)
  • Required: Yes
  • Default value: undefined

A fault-tolerant component that is rendered when the component fails to load or render.

Note: This component only renders this fault-tolerant component on the client side when rendering fails.

Configuration

ssr

  • Type: false
  • Is it required: No
  • Default value: undefined

@module-federation/modern-js will automatically add SSR related build presets based on server.ssr in modern.js config.

If the current project only needs to load MF in the CSR, you can set ssr: false to help progressive migration.

import { appTools, defineConfig } from '@modern-js/app-tools';
import { moduleFederationPlugin } from '@module-federation/modern-js';

// https://modernjs.dev/en/configure/app/usage
export default defineConfig({
  dev: {
    port: 3050,
  },
  runtime: {
    router: true,
  },
  server: {
    ssr: {
      mode: 'stream',
    },
  },
  plugins: [
    appTools(),
    moduleFederationPlugin({ ssr: false })
  ],
});