Micro frontend

How to Build Micro-Frontends Using Webpack Module Federation (with Full Code Example)

🧠 What is Micro-Frontend?

Micro-Frontend architecture breaks a frontend app into independent, deployable features owned by separate teams β€” much like microservices in the backend.

Instead of a monolithic React or Angular app, you build small frontends (e.g., dashboard, login, profile) and compose them together.

βš™οΈ Why Use Webpack Module Federation?

Webpack 5 introduces Module Federation, which allows an app to dynamically load code from another app at runtime.

This makes it possible to build fully independent micro frontends that:

  • Can be deployed separately
  • Share common dependencies
  • Work together seamlessly

πŸ“¦ Folder Structure

We’ll build:

  1. host-app β†’ Shell app that loads remote modules
  2. remote-app β†’ A micro frontend exposing a component
microfrontend-example/
β”œβ”€β”€ host-app/
└── remote-app/

1️⃣ Set Up the Remote App

This is your micro frontend that exposes a component (Hello) to other apps.

πŸ”§ remote-app/package.json

{
"name": "remote-app",
"version": "1.0.0",
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"webpack": "^5.0.0",
"webpack-cli": "^4.0.0",
"webpack-dev-server": "^4.0.0",
"html-webpack-plugin": "^5.5.0"
}
}

πŸ“Œ Why?
This sets up a standalone React project that uses Webpack 5 and React. This app will expose a component (Hello) using Webpack’s Module Federation.

Install dependencies:

cd remote-app
npm install

πŸ“„ remote-app/src/Hello.js

import React from "react";
const Hello = () => {
return <h2>Hello from Remote App πŸ‘‹</h2>;
};

export default Hello;

πŸ“Œ Why?
This is the component that we will expose to the host. It’s a simple React component that renders a message.

πŸ“„ remote-app/src/index.js

import React from "react";
import ReactDOM from "react-dom";
import Hello from "./Hello";

const App = () => <Hello />;

ReactDOM.render(<App />, document.getElementById("root"));

πŸ“Œ Why?
This is the entry point for remote-app, which directly renders the Hello component into the DOM when run directly (e.g., for testing).

πŸ› οΈ remote-app/webpack.config.js

const HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack").container.ModuleFederationPlugin;

module.exports = {
entry: "./src/index.js",
mode: "development",
devServer: {
port: 3001,
},
output: {
publicPath: "auto",
},
plugins: [
new ModuleFederationPlugin({
name: "remoteApp",
filename: "remoteEntry.js",
exposes: {
"./Hello": "./src/Hello",
},
shared: { react: { singleton: true }, "react-dom": { singleton: true } },
}),
new HtmlWebpackPlugin({
template: "./public/index.html",
}),
],
};

πŸ“Œ Why?
This is the heart of Module Federation.

  • name: Unique name for the remote app.
  • filename: This file (remoteEntry.js) is what other apps will load.
  • exposes: This tells Webpack which module we want to share (here, ./Hello).
  • shared: Ensures both host and remote share a single instance of React.

πŸ“„ remote-app/public/index.html

<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
</body>
</html>

πŸ“Œ Why?
Standard HTML container for rendering the React app.

Start the remote app:

npm start

πŸ“Œ Why?
Runs a dev server on http://localhost:3001. This will host the remoteEntry.js file and serve the app for direct viewing and testing.

2️⃣ Set Up the Host App

This is your container application β€” it dynamically loads remote components and renders them.

πŸ”§ host-app/package.json

Same as remote app β€” with dependencies like React, Webpack, etc.

Install everything:

cd host-app
npm install

πŸ“„ host-app/src/App.js

import React from "react";

// Dynamically load the remote component
const RemoteHello = React.lazy(() => import("remoteApp/Hello"));

const App = () => (
<React.Suspense fallback={<div>Loading...</div>}>
<RemoteHello />
</React.Suspense>
);
export default App;

πŸ“Œ Why?
We use React.lazy() to dynamically import the Hello component from the remote app at runtime.
This shows a fallback while the remote component is loading. React’s Suspense is needed when using React.lazy.

πŸ“„ host-app/src/index.js

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

ReactDOM.render(<App />, document.getElementById("root"));

πŸ“Œ Why?
Standard React entry point. Mounts the app to the HTML div.

πŸ› οΈ host-app/webpack.config.js

const HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack").container.ModuleFederationPlugin;

module.exports = {
entry: "./src/index.js",
mode: "development",
devServer: {
port: 3000,
},
output: {
publicPath: "auto",
},
plugins: [
new ModuleFederationPlugin({
name: "hostApp",
remotes: {
remoteApp: "remoteApp@http://localhost:3001/remoteEntry.js",
},
shared: { react: { singleton: true }, "react-dom": { singleton: true } },
}),
new HtmlWebpackPlugin({
template: "./public/index.html",
}),
],
};

πŸ“Œ Why?

  • remotes: This tells the host app where to load modules from.
  • remoteApp@http://localhost:3001/remoteEntry.js: This matches what remote-app exposed β€” we can now load remoteApp/Hello.

πŸ“„ host-app/public/index.html

<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
</body>
</html>

πŸ“Œ Why?
Root container where React app will render in the browser.

Start the host app:

npm start

πŸ“Œ Why?
Starts the host app at http://localhost:3000, which will now load and render the Hello component from the remote app.

βœ… Final Behavior

When you open the browser to http://localhost:3000, this happens:

It renders the component inside the host UI.

host-app loads.

It reads remoteEntry.js from http://localhost:3001.

It loads the Hello component from the remote.

βœ… You should see:

Hello from Remote App πŸ‘‹

🧠 Summary of Each Step

StepActionPurpose
1Create remote-appA micro-frontend with an exposed component
2Use ModuleFederationPluginMake component available to others
3Start remote-appServe the remoteEntry.js file
4Create host-appContainer app to load remote content
5Configure remotesLink host to remote
6Use React.lazyDynamically load remote component
7Start host-appView full system in action

Similar Posts