Lazy Load and Prefetch Components

This chapter introduces how to use createLazyComponent to load remote React components on demand in a host application.

createLazyComponent is a higher-level loading API for React component scenarios. Compared with directly loading a module and wiring up React.lazy, Suspense, and error boundaries yourself, it builds those pieces into the API and additionally provides:

  • Declarative loading and fallback: no need to manually wrap React.Suspense and error boundaries
  • Data prefetching: use prefetch to start dependency requests before the component loads, avoiding request waterfalls where data is requested only after the component is ready
  • SSR control: precisely control whether remote components render on the server, helping avoid CSS flickering issues

Installation

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

Basic Usage

Step 1: Register lazyLoadComponentPlugin

Register the lazyLoadComponentPlugin plugin at runtime to enable createLazyComponent and prefetch APIs.

import { getInstance } from '@module-federation/runtime';
import { lazyLoadComponentPlugin } from '@module-federation/bridge-react/data-fetch';

const instance = getInstance();
// Register lazyLoadComponentPlugin plugin
instance.registerPlugins([lazyLoadComponentPlugin()]);

Step 2: Call createLazyComponent

After registering the lazyLoadComponentPlugin plugin, you can create lazy-loaded components using the instance.createLazyComponent method.

import { getInstance } from '@module-federation/runtime';
import { lazyLoadComponentPlugin } from '@module-federation/bridge-react/data-fetch';

const instance = getInstance();
// After registering lazyLoadComponentPlugin plugin, you can use `createLazyComponent` or `prefetch` APIs
instance.registerPlugins([lazyLoadComponentPlugin()]);

// Use instance.prefetch for remote module data prefetching
instance.prefetch({
  id: 'dynamic_remote'
});

// Use instance.createLazyComponent for lazy loading remote modules
const LazyComponent = instance.createLazyComponent({
  loader: () => loadRemote('dynamic_remote'),
  loading: 'loading...',
  fallback: ({ error }) => {
    if (error instanceof Error && error.message.includes('not exist')) {
      return <div>fallback - not existed id</div>;
    }
    return <div>fallback</div>;
  },
});

createLazyComponent API Reference

Function Signature

function createLazyComponent<T, E extends keyof T>(
  options: CreateLazyComponentOptions<T, E>
): React.ComponentType<ComponentProps>

createLazyComponent

This API requires registering the lazyLoadComponentPlugin plugin first before it can be called.

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

type CreateLazyComponentOptions<T, E extends keyof T> = {
  loader: () => Promise<T>;
  loading: React.ReactNode;
  delayLoading?: number;
  fallback: ReactNode | ((errorInfo: ErrorInfo) => ReactNode);
  export?: E;
  dataFetchParams?: DataFetchParams;
  noSSR?: boolean;
  injectScript?: boolean;
  injectLink?: boolean;
};

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

type DataFetchParams = {
  isDowngrade: boolean;
} & Record<string, unknown>;

type ErrorInfo = {
  error: Error;
  errorType: number;
  dataFetchMapKey?: string;
};

In addition to loading components, this function also supports the following capabilities:

  1. In SSR mode, it will inject the corresponding producer's style tags/script resources, which can help avoid CSS flickering issues caused by streaming rendering and accelerate PID (First Paint Interactive Time).
  2. If the producer has a data fetching function, it will automatically call this function and inject the data.
import React, { FC, memo, useEffect } from 'react';
import { getInstance } from '@module-federation/enhanced/runtime';
import { ERROR_TYPE } from '@module-federation/bridge-react/data-fetch';

const instance = getInstance();
const LazyComponent = instance.createLazyComponent({
  loader: () => import('remote/Image'),
  loading: <div>loading...</div>,
  fallback: ({error,errorType,dataFetchMapKey}) => {
    console.error(error)
    if(errorType === ERROR_TYPE.LOAD_REMOTE){
      return <div>load remote failed</div>
    }
    if(errorType === ERROR_TYPE.DATA_FETCH){
      return <div>data fetch failed, the dataFetchMapKey key is: {dataFetchMapKey}</div>
    }
    return <div>error type is unknown</div>;
  },
});

const App: FC = () => {
  return <>
    <LazyComponent />
  </>;
};
export default App;

loader

  • Type: () => Promise<T>
  • Required: Yes
  • Default: undefined

Function to load remote components, typically ()=>loadRemote(id) or ()=>import(id).

loading

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

Sets the module loading state.

delayLoading

  • Type: number
  • Required: No
  • Default: undefined

Sets the delay time for displaying loading state in milliseconds. If the loading time is less than this time, the loading state will not be displayed.

fallback

  • Type: (({ error }: { error: ErrorInfo}) => React.ReactElement)
  • Required: Yes
  • Default: undefined

The error component rendered when component loading or rendering fails.

export

  • Type: string
  • Required: No
  • Default: 'default'

If the remote component is a named export, you can specify the component name to export through this parameter. By default, it loads the default export.

dataFetchParams

  • Type: DataFetchParams
  • Required: No
  • Default: undefined

If the remote component has a data fetching function, this will be passed to the data fetching function when set.

noSSR

  • Type: boolean
  • Required: No
  • Default: false

When set to true, this component will not render in SSR scenarios.

injectScript

  • Type: boolean
  • Required: No
  • Default: false

In SSR environment, if set to true, the created component will inject the corresponding script resources.

For example, if remote/button has __federation_button.js, then in the HTML returned by SSR, the corresponding script will be injected before the component to accelerate interaction speed.

<script async src="__federation_button.js" crossOrigin="anonymous"/>
<button>remote button</button>
  • Type: boolean
  • Required: No
  • Default: true

In SSR environment, if set to true, the created component will inject the corresponding style resource links.

For example, if remote/button has __federation_button.css, then in the HTML returned by SSR, the corresponding link will be injected before the component to avoid page flickering issues.

<link href="__federation_button.css" rel="stylesheet" type="text/css">
<button>remote button</button>

prefetch

This API requires registering the lazyLoadComponentPlugin plugin first before it can be called.

  Type declaration
type PrefetchOptions = {
  id: string;
  dataFetchParams?: DataFetchParams;
  preloadComponentResource?: boolean;
};
type DataFetchParams = {
  isDowngrade: boolean;
  _id?: string;
} & Record<string, unknown>;

Preload component resource files and the component's data loader.

import React, { FC, memo, useEffect } from 'react';
import { getInstance } from '@module-federation/enhanced/runtime';

const instance = getInstance();

instance.prefetch({
  id: 'remote/Image',
  preloadComponentResource: true,
});

id

  • Type: string
  • Required: Yes
  • Default: undefined

The id of the component to preload.

preloadComponentResource

  • Type: boolean
  • Required: No
  • Default: false

Whether to preload the component's resource files.

dataFetchParams

  • Type: DataFetchParams
  • Required: No
  • Default: undefined

If the remote component has a data fetching function, this will be passed to the data fetching function when set.