In this blog, we’ll use Module Federation and Bit to implement a runtime integration of Micro Frontends.
We'll cover the following topics:
- Using Bit’s templates to generate host applications and remote modules
- Managing ModFed shared dependencies as a Bit component
- Creating a “plugin system” via shared types that allows remote modules to integrate into the host application in various ways
- Running remote modules in development within the context of their host application
The repos maintaining the Bit workspaces for the shared components, host app, and remote modules
Host Apps and Remote Modules
The host app and remote modules were generated using pre-configured templates (made available by Bit):
npx @teambit/bvm install # install Bit
bit init my-modfed-solution # create a new Bit workspace
cd my-modfed-solution
Add the following to your workspace.jsonc
to make the ModFed templates available in your workspace:
"teambit.generator/generator": {
"envs": [
"frontend.module-federation/envs/mf-react-env"
]
}
Run the following commands:
bit install # install the workspace dependnecies
bit create modfed-remote-mfe storefront # generate a remote module
bit create modfded-remote-mfe blog
bit create modfed-host-app shell-app # generate a host app
Run
bit templates
to list the available ModFed templates
To list the available apps (and remote modules), run:
bit app list
The output lists the component IDs and their corresponding app names:
┌─────────────────────────────────────────────────┬─────────────────────┐
│ id │ name │
├─────────────────────────────────────────────────┼─────────────────────┤
│ bit-bazaar.storefront/storefront │ storefront │
├─────────────────────────────────────────────────┼─────────────────────┤
│ bit-bazaar.blog/blog │ blog │
├─────────────────────────────────────────────────┼─────────────────────┤
│ bit-bazaar.shell-app/shell-app │ shell-app │
└─────────────────────────────────────────────────┴─────────────────────┘
You can run the apps locally by using their app name:
bit run storefront
Shared Dependencies
Our solution consists of many shared dependencies configured to be excluded from the app bundles and loaded as separate chunks. This is one of ModFed’s strengths. It allows us to optimize our bundle size, maintain consistency, and avoid conflicts between versions of the same module.
Our shared dependencies are maintained as a Bit component shared across projects (the host app and the remote modules). This allows teams to maintain consistency while working independently.
The list of shared dependencies consists primarily of runtime libs and a design system:
The ‘shared dependencies’ component (which lists the shred deps) is used by the host app config and remote modules config
https://bit.cloud/bit-bazaar/shell-app/shared-dependencies/~code/shared-dependencies.ts
For example:
/**
* @filename: storefront.bit-app.ts
* @component-id: bit-bazaar.storefront/storefront
*/
import { MfReact } from '@frontend/module-federation.react.apps-types.mf-rspack';
/* import the 'shared dependnecies' components */
import { shellAppSharedDependencies } from '@bit-bazaar/shell-app.shared-dependencies';
export default MfReact.from({
name: 'storefront',
clientRoot: './storefront.app-root.js',
moduleFederation: {
exposes: {
// ...
},
shared: shellAppSharedDependencies,
}
});
A Shared Design System
Our component library and theme are based on Material UI. They are maintained in the “design” scope and shared across Micro Frontends.
The “design” scope which contains the shared UI components and themes
Shared Context
The ‘Theme Provider,’ ‘Auth Provider,’ and other context components are part of the “host app” or “shell app.” As such, they are maintained by the “shell app” team.
The context for all MFEs (provided by the shell app)
Teams working on MFEs do not need to bother with authentication, authorization, or any other shared functionality. The “host” or “shell” team provides it all to them.
For example, if team Storefront needs to implement functionality based on the user auth, they would explore the ‘shell app’ scope and look for the proper “SDK”.
Routing and Navigation
The shell app provides a sort of “plugin system” where Micro Frontends (remote modules) can integrate into it in ways that go beyond a simple link. It does so by providing the types for each “plugin”.
For example, a remote module can implement a “navigation item” interface that includes its navigation options.
This can then be exposed for the shell app (which will load it at runtime):
/**
* @filename: blog.bit-app.ts
* @component-id: bit-bazaar.blog/blog
*/
export default MfReact.from({
name: 'blog',
clientRoot: './blog.app-root.js',
moduleFederation: {
exposes: {
/**
* the MFE navigation exposed to be loaded
* by the shell app at runtime
**/
'./blognav': './navitem.js',
/**
* the main chunk of the 'blog' MFE
**/
'./blog': './blog.js',
},
shared: shellAppSharedDependencies,
},
deploy: Netlify.deploy(netlifyConfig),
});
The routing is handled at the level that suits the module. For example, the shell app only handles routing to /blog/*
and /storefront/*
. It does not determine the routing “inside” each MFE (such as storefront/products
).
/**
* @filename: shell-app.tsx
* @component-id: bit-bazaar.shell-app/shell-app
*/
export function ShellApp() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Homepage />} />
<Route path="store/*" element={<Store />} />
<Route path="blog/*" element={<Blog />} />
<Route path="*" element={<div>Not Found</div>} />
</Route>
</Routes>
</BrowserRouter>
);
Accordingly, remote modules, such as the blog
, are not responsible for the /blog/*
routing (the routing to the blog MFE)—only for nested routes.
/**
* @filename: blog.tsx
* @component-id: bit-bazaar.blog/blog
*/
export function Blog() {
return (
<Routes>
<Route path="articles" element={<ArticlesPage />} />
<Route path="categories" element={<CategoriesPage />} />
</Routes>
);
}
DevX
For the ultimate dev experience, each team uses a “Platform” component to consume an immutable version of the shell app and possibly other remote modules.
This provides the MFEs with the proper context to run in development. It ensures a consistent and seamless dev experience while properly enforcing permissions and access control (e.g., the 'blog' team cannot modify the 'storefront' MFE or the shell app).
https://bit.cloud/bit-bazaar/storefront/storefront-platform/~code/shell-platform.bit-app.ts
The 'shell-app' as an immutable dependency of the ‘storefront-platform’ used by the ‘storefront’ team for ‘storefront’ development in full context
For example, the ‘storefront’ team are able to run the ‘storefront’ MFE in its full context (shell app and even other MFEs) by running the 'platform' app maintained by them (for development only):
bit run storefront-platform
You can generate a 'Platform' component using the template provided by the ModFed env (the one configured as a generator at the beginning of this blog):
bit create modfed-platform my-platform