Data Fetching
Remote modules typically want to handle data fetching on their own: the consumer is only responsible for loading and rendering, without having to know what data the remote depends on, and without each consumer reimplementing the same fetching logic.
In CSR scenarios, a remote can simply call useEffect inside the component to fire the request. In SSR scenarios, however, useEffect does not run on the server, so the remote cannot fetch data on the server before rendering.
Mainstream SSR frameworks (Next.js, Remix, Modern.js, etc.) usually provide route-level data prefetching: when a route matches, a data-loading function runs first, and its data is injected into the matching route component. But this mechanism depends on each framework's own router, and modules exposed by Module Federation do not live in the host's route tree, so this cannot be reused directly.
To solve this, Module Federation provides component-level data fetching capabilities, so developers can fetch data and render components in SSR scenarios.
Modules exposed via Module Federation fall into two broad categories:
- Components: standalone UI components, utility functions, etc., without their own router.
- Applications: sub-applications with a complete router, typically exposed and consumed via Bridge.
The capability described in this document targets the former — "component-level" data fetching. See the demo for a detailed walkthrough.
How to Use
Data fetching supports both SSR and CSR scenarios. The sections below cover producer and consumer separately:
Producer
Data fetching file convention
Data fetching uses a file convention:
- Each exposed module can be paired with a
.datafile of the same name. - A function exported from a
.datafile is called a Data Loader, and the build plugin recognizes this convention. - When an exposed component is loaded, the runtime first calls the Data Loader to retrieve the data, injects it into the component, and then renders.
Example file layout:
The convention file must export a function named fetchData, which runs before the remote component renders:
The function is called before the component renders, and the runtime injects the returned data object into the component's props. The default key is mfData:
Default loader function parameters
By default, parameters are passed to the loader function. The type is DataFetchParams, which includes the following field:
isDowngrade: Indicates whether the current execution context is in a fallback mode. For example, if Server-Side Rendering fails, a new request is sent from the browser to the server to call the loader function, in which case the value istrue.
Using Data Loader in Different Environments
The loader function can be executed on the server or in the browser. A loader function executed on the server is called a Server Loader, and one executed in the browser is called a Client Loader.
In CSR applications, the loader function is executed in the browser, so they are all Client Loaders by default.
In SSR applications, the loader function is only executed on the server, so they are all Server Loaders by default. In SSR, Module Federation directly calls the corresponding loader function on the server. When switching routes in the browser, Module Federation sends an HTTP request to the SSR service, which also triggers the loader function on the server.
- Simplified Usage: It ensures that the data fetching method in SSR applications is isomorphic, so developers don't need to differentiate code execution based on the environment.
- Reduced Browser Bundle Size: Logic code and its dependencies are moved from the browser to the server.
- Improved Maintainability: Moving logic code to the server reduces the direct impact of data logic on the front-end UI. It also prevents accidentally including server-side dependencies in the browser bundle or vice versa.
Using Client Loader in SSR Applications
By default, in SSR applications, the loader function is only executed on the server. However, in some scenarios, developers may want requests from the browser to go directly to the data source without passing through the SSR service. For example:
- To reduce network consumption in the browser by directly requesting the data source.
- The application has a data cache in the browser and does not want to request data from the SSR service.
Module Federation supports adding an additional .data.client file in SSR applications, which exports the named function in the same way. In this case, if the Data Loader on the server fails and falls back, or when switching routes in the browser, the application will execute this function in the browser like a CSR application, instead of sending another data request to the SSR service.
To use a Client Loader, there must be a corresponding Server Loader, and the Server Loader must be defined in a .data file.
Producer Consuming Its Own Application Data
A producer may also be visited as a standalone page. In that case, it needs to be able to fetch data in both scenarios:
- When loaded by a consumer, data is injected via the Data Loader.
- When visited directly, data is fetched by the producer's own framework mechanism.
For example, if the producer uses Modern.js, it needs to support both being loaded by a consumer and being visited directly as a producer page. This can be done as follows:
- Create a
page.data.tsfile in the producer's page directory and export a function namedloader:
Modern.js convention is to export a loader function from page.data.ts to fetch data.
- Consume this data on the producer's
page:
Consumer
Currently, only Modern.js supports data fetching in SSR environments.
In the consumer, we need to use the createLazyComponent API to load the remote component and fetch its data.
Passing custom parameters
In addition to the data structure the runtime injects by default, the consumer can pass custom parameters through to the loader function via the dataFetchParams field on createLazyComponent — for example, the host-side context such as user identifier, language, or tenant.
This field is merged with the default parameters and passed to the loader.
FAQ
Application-Level Data Fetching?
For application-level modules, we prefer to use RSC (React Server Components) to make the functionality more complete. This feature is currently under exploration, so please stay tuned.
Is Nested Producer Supported?
No, it is not supported.
Are there other plugins for producers besides the Rslib plugin and the Modern.js plugin?
Currently, only the Rslib and Modern.js plugins can create a Data Loader.