A micro frontends sample implementation of The Tractor Store built with Module Federation and React. It's based on the Piral version.
Live Demo: TODO
List of techniques used in this implementation.
Aspect | Solution |
---|---|
🛠️ Frameworks, Libraries | React, Module Federation, Rspack |
📝 Rendering | SPA |
🐚 Application Shell | react-router |
🧩 Client-Side Integration | Module Federation |
🧩 Server-Side Integration | none |
📣 Communication | Custom Events, Slots |
🗺️ Navigation | SPA, One MF per Team, react-router |
🎨 Styling | Self-Contained CSS (No Global Styles) |
🍱 Design System | None |
🔮 Discovery | Zephyr Cloud |
🚚 Deployment | Static Page |
👩💻 Local Development | Rspack's devServer |
This implementation is deliberately kept simple to focus on the micro frontends aspects. URLs are hardcoded, components could be more DRY and no linting, testing or type-safety is implemented. In a real-world scenario, these aspects should be addressed properly.
Several performance optimizations could still be applied, however, in the out-of-the-box state with three micro frontends and multiple components / pages included we'll end up with a lighthouse score of 100/100, which is great.
Fork this repository. Then use pnpm
to bootstrap the mono repo. Make sure to have pnpm
(v9) installed for this.
Run the following command inside the repository:
pnpm install
- Build the
explore
app first
Because this project has recursive dependencies, you must comment out remotes that's yet build, and build the remotes first -- otherwise Zephyr won't be able to map your remotes against your host applications (or the application consumes it).
Note that each build
command will trigger a deployment with Zephyr.
In the rspack.config.ts of explore
, comment out the remotes (it's not built yet):
new ModuleFederationPlugin({
name,
filename: 'remoteEntry.js',
shared: ['react', 'react-dom', 'react-router', 'react-router-dom'],
// remotes: {
// tractor_v2_checkout: 'tractor_v2_checkout@http://localhost:3001/remoteEntry.js',
// },
exposes: {
'./HomePage': path.resolve(__dirname) + '/src/HomePage.tsx',
'./CategoryPage': path.resolve(__dirname) + '/src/CategoryPage.tsx',
'./StoresPage': path.resolve(__dirname) + '/src/StoresPage.tsx',
'./Recommendations': path.resolve(__dirname) + '/src/Recommendations.tsx',
'./StorePicker': path.resolve(__dirname) + '/src/StorePicker.tsx',
'./Header': path.resolve(__dirname) + '/src/Header.tsx',
'./Footer': path.resolve(__dirname) + '/src/Footer.tsx',
},
}),
and run
WITH_ZE=true pnpm --filter tractor_v2_explore run build
- Build
checkout
app
At this point explore
app has been build and checkout
app is only consuming explore
, we build the checkout
app next:
WITH_ZE=true pnpm --filter tractor_v2_checkout run build
- Uncomment
checkout
inexplore
app
Run explore app's build again to build it to a full app, but uncomment the remotes in configuration file:
new ModuleFederationPlugin({
name,
filename: 'remoteEntry.js',
shared: ['react', 'react-dom', 'react-router', 'react-router-dom'],
remotes: {
tractor_v2_checkout: 'tractor_v2_checkout@http://localhost:3001/remoteEntry.js',
},
exposes: {
'./HomePage': path.resolve(__dirname) + '/src/HomePage.tsx',
'./CategoryPage': path.resolve(__dirname) + '/src/CategoryPage.tsx',
'./StoresPage': path.resolve(__dirname) + '/src/StoresPage.tsx',
'./Recommendations': path.resolve(__dirname) + '/src/Recommendations.tsx',
'./StorePicker': path.resolve(__dirname) + '/src/StorePicker.tsx',
'./Header': path.resolve(__dirname) + '/src/Header.tsx',
'./Footer': path.resolve(__dirname) + '/src/Footer.tsx',
},
}),
WITH_ZE=true pnpm --filter tractor_v2_explore run build
- Build
decide
Since explore
and checkout
are both being built, you can run this command to build decide
(decide
consumes explore
and checkout
, they are both built at this point ):
WITH_ZE=true pnpm --filter tractor_v2_decide run build
- Build
app
All the remotes are being built now and Zephyr will be able to map your remote application in output bundle, you can build app
directly by running:
WITH_ZE=true pnpm --filter tractor_v2_app run build
You can deploy to Zephyr Cloud building the packages:
WITH_ZE=true pnpm build
Or deploy to cloud on each save by running:
WITH_ZE=true pnpm serve
We have now deployed everything! You can open our Chrome Extension,
Select this application on Chrome Extension, toggle on Live reload
to inspect file changes while running WITH_ZE=true pnpm serve
The Piral documentation page has a tutorial on the sample that this example was based on. Make sure to follow and understand the tutorial before going deep into this sample.