This project demonstrates a Micro Frontend architecture using Webpack 5 Module Federation.
The architecture consists of three completely independent React applications:
- Shell App (Host) -
port 3000: Functions as the main host container. It contains the main navigation and lazily loads the other micro frontend applications at runtime. - Profile App (Remote) -
port 3001: A remote standalone app that exposes aProfileComponent. - Marketplace App (Remote) -
port 3002: A remote standalone app that exposes aMarketplaceComponent.
- Webpack 5 Module Federation Plugin.
- Three independent apps with a clear, separate folder structure (
shell,profile,marketplace). - Apps are independently developable, deployable, and versionable.
- Lazily loading of remote components
ProfileComponent&MarketplaceComponentusing ReactlazyandSuspense. - Simple navigation UI in the Shell App using React Router framework to switch views.
- Dynamic Resolution:
remoteEntry.jsURLs are NOT hardcoded in the webpack configuration. They are resolved via an environment configuration file (config.json), read at bootstrap time. - Graceful Failover: Implementing React
ErrorBoundaryspecifically designed to handle network failures when lazy loading chunks. - Both standard app running and isolated running are supported (both modules expose components but also render themselves out via
bootstrap.jsin case of isolated development).
Make sure node and npm are installed.
Each app (shell, profile, marketplace) is a separate project, but you can set up everything simply by running install at the root level if you modify package.json with Workspaces. For this repository, simply run npm run install or since we have initialized them manually, dependencies are already installed. Note that concurrently has been configured to run them all at once.
If you are cloning this repository for the first time, run:
npm install concurrently -D
cd shell && npm install
cd ../profile && npm install
cd ../marketplace && npm install
cd ..From the root directory of the workspace, run:
npm startThis command utilizes concurrently to start the development servers for all 3 apps simultaneously. Wait for the servers to compile.
- Shell App: http://localhost:3000
- Upon initial load, it will fetch
/config.json, which points to the remote entries. - In the Network tab, you will initially only see Shell chunks.
- Clicking Profile will dynamically trigger a network request for
http://localhost:3001/remoteEntry.jsand subsequently load the Profile chunk. - Clicking Marketplace lazy-loads the Marketplace chunk identically format.
- Upon initial load, it will fetch
- Profile App (Standalone): http://localhost:3001
- Marketplace App (Standalone): http://localhost:3002
Usually, remotes are hardcoded into webpack.config.js:
remotes: {
profile: 'profile@http://localhost:3001/remoteEntry.js'
}This prevents environment switching (dev -> staging -> prod) without rebuilding the Docker container / code.
Instead of hardcoding, we use Promises inside Webpack Config:
- At runtime before the application bootstraps (
src/index.js), we execute an HTTPfetch()request to/config.json. - This
config.jsonstores the raw URLs:
{
"profile": "http://localhost:3001/remoteEntry.js",
"marketplace": "http://localhost:3002/remoteEntry.js"
}- These URLs are stored inside
window.__MF_REMOTES__. - Our
webpack.config.jsconsumes this global variable and synchronously injects the corresponding<script>tag dynamically using Promises when the specific remote bundle is actually triggered by React via User Navigation. - Furthermore, any failure correctly triggers
script.onerror()capturing the Promise rejection, eventually forwarding the failure state directly over to our<ErrorBoundary />ensuring the host does not crash from missing remotes!