How mfeflow works
This page explains what happens between a visitor opening the shell and a remote appearing on screen. It also covers how the apps are built and how development differs from production. It is an explanation page, so it has no steps to follow.
What happens when the shell boots?
The shell's entry file calls main() in bootstrap.tsx. That function does four things in order. It reads the registry index, applies any development overrides, registers the remotes with Module Federation, and renders the app.
The registry index is a list of remotes with a name and an entry URL. A server or CDN edge can write it into the page as window.__MFE_REGISTRY__, which saves one request. Otherwise the shell fetches /registry/index.json from its own origin.
The runtime config can name more than one index, for example one on a CDN. The shell tries the primary for up to 1.5 seconds, then the fallback, and reports the switch. Reading the config never holds up boot for more than 1.5 seconds. The registry index describes the format and the fallback.
Registering does not download anything. A remote's code loads only when a route renders its boundary. That boundary waits up to five seconds, then shows a fallback and reports the failure. The rest of the page keeps working.
How are the apps built?
Every app uses Rsbuild with the Module Federation 2 plugin. You do not write that configuration. Two functions from @mfeflow/rsbuild return it: createShellConfig and createRemoteConfig.
The presets set these defaults. Each is a decision about cost or safety.
Remote names must be lowercase letters, digits and underscores. The preset throws on anything else. A remote must expose at least one module.
How are shared singletons checked?
A singleton is a package that must exist once per page, such as React. The presets share a fixed list of packages and set a required version from what is installed. Shared singletons explains why.
There are two checks. At build and review time, mfeflow doctor compares singleton versions across apps (E301). mfeflow check-compat compares each remote's ranges with its shell (E302). Both run offline from your files.
At run time, the runtime package can read Module Federation's share scope. It reports packages that are missing or loaded in more than one version.
In a generated project, the shell runs this check in development only. It reports a duplicate or missing React in the console and in the dev panel. The check is not loaded in production builds, so it costs nothing there.
The storefront example in the repository also runs it on demand with ?mfeflow-assert, for its tests.
How does development differ from production?
In development, mfeflow dev starts every app and builds a dev registry from the project. The shell build puts that registry into the HTML. Remotes load from their own localhost ports.
In production, nothing runs the apps for you. You serve the shell and each remote as static files, and you serve a registry index. The shell expects remotes under /mfe/<name>/ on its own origin. Deploy covers several ways to do it.
Overrides exist only in development. Adding ?mfe-override=cart@http://localhost:4312/mf-manifest.json to the URL points one remote at your local build. See Develop locally.
What is in the generated shell?
The shell has a small path-based router in App.tsx and a route table. It does not install a router library. You can replace it. Features such as route-factory add typed links.
The generated App.tsx also mounts a development-only panel, DevOverlay, from the runtime package. It lists the registered remotes and their errors. Press Alt+Shift+M to toggle it.
Where to go next
- Look at the files: Project structure.
- Learn the underlying idea: Module Federation.
- A build or load failed: Troubleshooting.