Sitemap

Micro Frontend Architecture: A Beginner’s Guide to Host, Remote, and Module Federation

14 min readOct 23, 2026

--

Press enter or click to view image in full size
Micro Frontend Architecture: A Beginner’s Guide to Host, Remote, and Module Federation

Imagine opening Amazon or Flipkart.

You see a navigation bar, product categories, a search bar, product listings, recommendations, a shopping cart, and a checkout flow.

Now imagine that different teams are responsible for developing these features.

One team works on the product listing page. Another develops the shopping cart. A third manages checkout, while another team handles search.

How can all these teams develop, test, and deploy their features independently while delivering a single, consistent experience to the user?

One possible solution is Micro Frontend Architecture.

Micro Frontends apply the ideas of microservices to frontend development. Instead of building an entire user interface as one tightly coupled application, we divide it into smaller, independently manageable frontend applications.

In this article, we’ll explore the concept from scratch, understand why it is useful, learn about Host and Remote applications, and see how Webpack Module Federation helps different frontend applications work together.

We’ll also build a small conceptual example using React and Webpack.

No prior knowledge of Micro Frontends is required.

1. First, Understand the Problem

Let’s consider an e-commerce application similar to Amazon or Flipkart.

A simplified version might contain these features:

  • Header and navigation
  • Product search
  • Product listing
  • Product details
  • Shopping cart
  • Checkout
  • User account

Initially, a company might build all these features inside a single React application.

The project could look something like this:

ecommerce-app/
src/
components/
Header.jsx
SearchBar.jsx
ProductCard.jsx
Cart.jsx
Checkout.jsx
pages/
Home.jsx
Products.jsx
ProductDetails.jsx
App.jsx

This is a perfectly reasonable architecture for many applications.

However, as the application grows, several challenges can emerge.

Problem 1: Multiple teams work on the same codebase

Imagine five teams working on the same frontend application.

The product team modifies product components, the checkout team changes checkout logic, and the cart team updates cart functionality.

Changes can overlap, dependencies can become complicated, and teams may need to coordinate releases.

Problem 2: Independent deployment becomes difficult

Suppose the shopping cart team finishes a feature today.

However, the application contains other features that are still under development.

Depending on how the project is structured and deployed, the cart team may have to wait for the larger application release.

Problem 3: The codebase becomes harder to maintain

As the application grows, shared components, business logic, dependencies, and team responsibilities can become tightly coupled.

This doesn’t mean every large React application will develop these problems. Good modular design can keep a monolithic frontend maintainable for a long time.

But at a certain organizational and technical scale, a different architecture may become useful.

This brings us to Micro Frontends.

2. What Is Micro Frontend Architecture?

Micro Frontend Architecture is an approach in which a large frontend application is divided into smaller frontend applications or independently developed features that work together to deliver a unified user experience.

Instead of one team owning the entire frontend, different teams can own different parts of the application.

For example:

              E-Commerce Application
|
+--------------+--------------+
| | |
Products Cart Checkout
| | |
Team A Team B Team C
| | |
Frontend Frontend Frontend
Application Application Application

Each frontend can have its own:

  • Components and business logic
  • Development workflow
  • Tests and build process
  • Team ownership
  • Release process, depending on the architecture

The different parts are then composed into a single experience for the user.

The important point is that the user sees one application, even though multiple frontend applications may contribute to it.

A real-world example: An online shopping platform

Imagine an e-commerce platform with the following ownership:

                   Shopping Website
|
+----------------+----------------+
| | |
Product UI Cart UI Checkout UI
| | |
Team A Team B Team C
| | |
Product data Cart state Order flow

The product team can improve product cards without needing to coordinate every small change with the checkout team.

The cart team can work on cart-related features independently, provided the teams maintain compatible integration contracts.

The checkout team can focus on the checkout experience.

This is the basic idea behind Micro Frontends.

3. Why Do We Need Micro Frontends?

Micro Frontends aren’t required for every application. A single React application is often the better choice for small and medium-sized projects.

Micro Frontends become valuable when an organization needs stronger team independence, clearer ownership, and more flexible delivery.

Let’s look at the main reasons.

1. Independent team ownership

Different teams can own different parts of the frontend.

For example:

Product Team → Product listing
Cart Team → Shopping cart
Checkout Team → Checkout experience

Each team can make decisions within its area without constantly modifying another team’s code.

2. Independent deployment

Suppose the cart team adds a new discount feature.

With a suitable Micro Frontend architecture, the team may be able to build and deploy the cart application independently of the product listing application.

The exact deployment model depends on how the applications are composed.

For example, a runtime-composed architecture can load a newly deployed remote application without requiring the host’s entire bundle to be rebuilt, provided the remote’s integration contract remains compatible.

3. Better ownership of large codebases

Instead of organizing every feature around one enormous frontend repository, teams can establish clearer boundaries.

These boundaries can make it easier to understand who owns a feature, which team handles bugs, and where new functionality belongs.

4. Technology flexibility

In some architectures, individual frontend applications can use different frameworks.

For example, one part might use React while another uses Vue.

However, this flexibility comes with costs. Different frameworks can increase bundle size, create inconsistent user experiences, and complicate integration.

Using the same framework across all Micro Frontends is often simpler.

5. Parallel development

Multiple teams can develop features simultaneously, reducing coordination overhead when their responsibilities and integration contracts are well defined.

Remember: Micro Frontends solve organizational and architectural problems. They don’t automatically make an application faster or its code better.

4. How Do Micro Frontends Work?

There are several ways to implement Micro Frontends.

For example:

  • Build-time integration
  • Runtime integration
  • Web Components
  • Client-side routing and separate applications
  • Server-side composition

One popular runtime integration approach in the Webpack ecosystem is Module Federation.

Before exploring it, let’s understand two important terms: Host and Remote.

What is a Host application?

The Host is the application that brings together features exposed by other frontend applications.

It may provide:

  • The main application shell
  • Navigation and routing
  • Global layout
  • Authentication integration
  • Shared application infrastructure

For our example, imagine that the Host is the main shopping website.

Host Application
|
+-- Header
+-- Navigation
+-- Product Remote
+-- Cart Remote

The Host can load features from other applications at runtime.

What is a Remote application?

A Remote is an application that exposes selected modules so that another application can consume them.

For example, the Product Remote might expose a product listing component.

Product Remote
|
+-- ProductList.jsx
+-- ProductCard.jsx
+-- Product-related logic

The Host can load the exposed ProductList component and render it as part of its own user interface.

A Remote doesn’t have to expose its entire application. It can expose specific components, utilities, or other modules.

Host vs Remote

+------------------------+------------------------+---------------------------+
| Feature | Host | Remote |
+------------------------+------------------------+---------------------------+
| Main responsibility | Composes the app | Exposes modules |
| Loads remote modules | Usually yes | Not necessarily |
| Exposes modules | Optional | Commonly does |
| Own build config | Yes | Yes |
| Independent deployment | Depends on architecture| Often supported |
| Example | Shopping website | Product listing app |
+------------------------+------------------------+---------------------------+

In simple terms:

  • Host: The main application that brings different frontend features together.
  • Remote: An application that exposes components or modules for other applications to consume.

5. What Is Webpack Module Federation?

Now that we understand Host and Remote applications, let’s explore the mechanism that connects them.

Webpack Module Federation allows separately built JavaScript applications to share and consume modules at runtime.

It was introduced in Webpack 5.

Traditionally, applications commonly bundle their dependencies during the build process. If one application needs code from another, that code may be imported through a package, shared library, or another integration mechanism.

Module Federation provides a way to expose selected modules from one build and consume those modules from another build at runtime.

For example:

                Host Application
|
|
Loads remote module
|
v
Remote Application
|
|
ProductList.jsx

The Host doesn’t necessarily need the Remote’s source code in its own repository.

Instead, it can load the Remote’s federation entry file, commonly named remoteEntry.js, and use the modules exposed by that Remote.

Let’s understand the key terms.

1. ModuleFederationPlugin

Webpack provides the ModuleFederationPlugin to configure federation.

It allows an application to define:

  • Its federation name
  • The remote entry filename
  • The modules it exposes
  • The remote applications it consumes
  • Dependencies shared between applications

2. remoteEntry.js

The Remote build generates a federation entry file, commonly called remoteEntry.js.

This file provides the runtime information needed to access the modules exposed by the Remote.

Think of it as the entry point through which the Host discovers and loads the Remote’s exposed modules.

It is not necessarily the complete application bundle. Additional JavaScript chunks may be downloaded when needed.

3. exposes

The exposes configuration specifies which modules a Remote makes available.

For example:

exposes: {
"./ProductList": "./src/ProductList.jsx"
}

This means that another application can reference the exposed module using the name ./ProductList.

4. remotes

The remotes configuration tells the Host where to find another application.

For example:

remotes: {
productApp: "productApp@http://localhost:3001/remoteEntry.js"
}

Here:

  • productApp is the local alias used by the Host.
  • productApp before @ is the Remote container name.
  • The URL points to the Remote’s federation entry file.

5. shared

The shared configuration lets applications share dependencies rather than always loading separate copies.

React is a common example.

shared: {
react: {
singleton: true
},
"react-dom": {
singleton: true
}
}

The singleton option tells the federation runtime to use a single shared instance of that dependency within the relevant sharing scope, subject to the configured versions and runtime negotiation.

This is especially important for React because separate incompatible React instances can cause problems when components and hooks are integrated across application boundaries.

For production applications, dependency versions and sharing requirements should be configured carefully.

6. Let’s Build a Simple Example Using React and Webpack

Let’s create two applications:

  1. Host Application — Displays the main page and loads the product component.
  2. Product Remote — Exposes a reusable ProductList component.

Our goal is simple:

Get Rohit Kuwar’s stories in your inbox

Join Medium for free to get updates from this writer.

When a user opens the Host application, it loads the ProductList component from the Remote application.

We’ll use Webpack 5 and React.

Project structure

micro-frontend-demo/
host/
src/
App.jsx
index.js
webpack.config.js

product-remote/
src/
ProductList.jsx
index.js
webpack.config.js

For simplicity, we’ll focus on the federation configuration and the React components rather than reproducing all the boilerplate required to configure two complete Webpack projects.

Step 1: Create the Product Remote component

Inside product-remote/src/ProductList.jsx:

import React from "react";

const products = [
{ id: 1, name: "Laptop", price: 65000 },
{ id: 2, name: "Headphones", price: 2500 },
{ id: 3, name: "Keyboard", price: 1500 }
];

function ProductList() {
return (
<section>
<h2>Products</h2>
{products.map((product) => (
<div key={product.id}>
<h3>{product.name}</h3>
<p>Price: ₹{product.price}</p>
</div>
))}
</section>
);
}

export default ProductList;

This is a simple React component that displays a list of products.

Normally, we might import this component directly into another React application.

But with Module Federation, we will expose it through the Remote application’s build configuration.

Step 2: Configure the Product Remote

Inside product-remote/webpack.config.js:

const HtmlWebpackPlugin = require("html-webpack-plugin");
const { ModuleFederationPlugin } = require("webpack").container;
const deps = require("./package.json").dependencies;

module.exports = {
mode: "development",
entry: "./src/index.js",
devServer: {
port: 3001,
historyApiFallback: true
},
output: {
publicPath: "auto"
},
resolve: {
extensions: [".js", ".jsx"]
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: "babel-loader"
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html"
}),
new ModuleFederationPlugin({
name: "productApp",
filename: "remoteEntry.js",
exposes: {
"./ProductList": "./src/ProductList.jsx"
},
shared: {
react: {
singleton: true,
requiredVersion: deps.react
},
"react-dom": {
singleton: true,
requiredVersion: deps["react-dom"]
}
}
})
]
};

Let’s understand the important configuration.

name: "productApp"

This identifies the Remote container.

filename: "remoteEntry.js"

This specifies the name of the federation entry file generated by Webpack.

exposes

exposes: {
"./ProductList": "./src/ProductList.jsx"
}

This exposes the product component so that another application can import it through Module Federation.

shared

We configure React and React DOM as shared dependencies to help avoid unnecessary duplicate framework instances.

publicPath: "auto"

This helps Webpack determine the public path for loading assets and additional chunks. It is useful when a Remote is served from a different location than the Host.

The configuration assumes a standard Webpack project with dependencies such as webpack, webpack-cli, webpack-dev-server, html-webpack-plugin, babel-loader, and the appropriate Babel presets already installed.

Step 3: Configure the Host application

Now let’s configure the main shopping website.

Inside host/webpack.config.js:

const HtmlWebpackPlugin = require("html-webpack-plugin");
const { ModuleFederationPlugin } = require("webpack").container;
const deps = require("./package.json").dependencies;

module.exports = {
mode: "development",
entry: "./src/index.js",
devServer: {
port: 3000,
historyApiFallback: true
},
output: {
publicPath: "auto"
},
resolve: {
extensions: [".js", ".jsx"]
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: "babel-loader"
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html"
}),
new ModuleFederationPlugin({
name: "hostApp",
remotes: {
productApp:
"productApp@http://localhost:3001/remoteEntry.js"
},
shared: {
react: {
singleton: true,
requiredVersion: deps.react
},
"react-dom": {
singleton: true,
requiredVersion: deps["react-dom"]
}
}
})
]
};

The most important part is:

remotes: {
productApp:
"productApp@http://localhost:3001/remoteEntry.js"
}

This tells the Host where to find the Product Remote.

The Remote must be running and serving its federation entry file at that URL.

In production, the URL would typically point to a deployed asset location or CDN rather than localhost.

Step 4: Import the Remote component in the Host

Inside host/src/App.jsx:

import React, { Suspense, lazy } from "react";

const ProductList = lazy(
() => import("productApp/ProductList")
);

function App() {
return (
<div>
<h1>My Shopping Website</h1>
<nav>
<a href="/">Home</a>
{" | "}
<a href="/cart">Cart</a>
</nav>
<Suspense fallback={<p>Loading products...</p>}>
<ProductList />
</Suspense>
</div>
);
}

export default App;

Let’s break this down.

import("productApp/ProductList")

This references the exposed component from the Remote application.

It isn’t a normal relative import such as:

import ProductList from "./ProductList";

Instead, Webpack resolves it through the configured federation relationship.

React.lazy()

This lets React load the component asynchronously.

Suspense

While the component is being loaded, React displays the fallback UI.

For example:

My Shopping Website

Loading products...

Once the component loads successfully, the product list appears.

Remember that React.lazy() and Suspense handle the component's loading state, but you should also design explicit error handling for failed Remote downloads or incompatible integrations.

Step 5: What happens when the application runs?

Assume both applications are running:

Host:
http://localhost:3000

Product Remote:
http://localhost:3001

The browser opens the Host application.

The Host discovers that ProductList comes from productApp.

It loads the Remote’s federation entry file, resolves the exposed module, and loads the required code.

The result looks like this:

Browser
|
v
Host Application :3000
|
| Loads remoteEntry.js
v
Product Remote :3001
|
| Provides ProductList
v
Host renders ProductList
|
v
User sees one shopping website

The user doesn’t need to know that the product component originated from another application.

The Host provides the surrounding page, and the Remote provides the product listing feature.

Important: This example illustrates runtime integration. To run it, both applications need valid Webpack configurations, React dependencies, entry files, HTML templates, Babel configuration, and scripts to start their development servers. The Remote must be available at the configured URL.

7. How Does Independent Deployment Work?

One of the major attractions of Micro Frontends is the ability to deploy different frontend features independently.

Imagine the following deployment setup:

                 Production Website
|
v
Host Application
|
v
Remote Entry URL
|
v
Product Remote

Suppose the product team changes the product card design.

The team builds and deploys a new version of the Product Remote.

If the Host continues loading the same stable Remote entry URL, it can load the updated implementation without rebuilding the Host, provided the Remote remains compatible with the Host’s expectations.

This is an important benefit, but it requires good release management.

For example, teams need to consider:

  • Backward-compatible exposed module APIs.
  • Remote availability and fallback behavior.
  • Dependency compatibility.
  • Cache invalidation for remote assets.
  • Versioning and rollback strategies.
  • Testing integration between the Host and Remote.

Independent deployment doesn’t mean there are no dependencies. It means teams can manage and release their parts more independently when those dependencies are well controlled.

8. Advantages of Micro Frontend Architecture

Let’s summarize the major benefits.

1. Independent development

Different teams can own different frontend features and work in parallel.

2. Independent deployment

A Remote can often be deployed separately from the Host in a runtime-composed architecture.

3. Clear ownership

Teams can maintain responsibility for their components, functionality, and release processes.

4. Incremental modernization

Organizations can gradually replace or modernize parts of a legacy frontend instead of rewriting the entire application at once.

5. Organizational scalability

As teams grow, clearer boundaries can reduce coordination overhead and allow teams to move more independently.

These benefits are most valuable when the application and organization are large enough to justify the extra architecture.

9. Disadvantages of Micro Frontend Architecture

Micro Frontends are not a free improvement.

They introduce complexity that a well-structured monolithic frontend may not need.

1. Increased architectural complexity

Instead of managing one frontend build, you may need to manage multiple builds, deployment pipelines, runtime dependencies, and integration contracts.

2. Dependency management

Different applications may use incompatible versions of React or other libraries.

Module Federation’s shared dependency configuration helps, but version negotiation and compatibility still require careful planning.

3. Performance overhead

Multiple frontend applications may introduce additional JavaScript, network requests, and runtime overhead.

Poorly designed Micro Frontends can make an application slower rather than faster.

4. Shared state becomes more complicated

Suppose the cart count appears in the global header, product pages, and checkout.

How should these parts share cart information?

Possible approaches include:

  • A shared backend API.
  • Explicit props and callbacks where appropriate.
  • A carefully designed shared state layer.
  • Custom browser events.
  • A shared state library, when the architecture justifies it.

There is no universal solution. The important thing is to define clear ownership and avoid creating tightly coupled global state across all frontend applications.

5. Inconsistent user experience

Different teams might implement different button styles, spacing, loading states, or accessibility behavior.

A shared design system and consistent interface guidelines can help maintain a unified experience.

6. Debugging becomes harder

An issue might originate in the Host, a Remote, a shared dependency, a network request, or an integration contract.

Teams need good logging, source maps, monitoring, and clear ownership to troubleshoot effectively.

7. Operational overhead

Multiple independently deployed applications mean more pipelines, monitoring, release coordination, and potentially more infrastructure to maintain.

For a small application, this overhead may outweigh the benefits.

10. When Should You Use Micro Frontends?

Micro Frontends are worth considering when:

  • Multiple teams own distinct frontend domains.
  • Teams need to release features independently.
  • The application is large and has clear domain boundaries.
  • A gradual migration from a legacy frontend is necessary.
  • The organization can support the added operational complexity.

They may be unnecessary when:

  • The application is small.
  • One team owns the entire frontend.
  • Features are tightly coupled.
  • A modular monolithic React application already meets the requirements.
  • Independent deployment offers little practical benefit.

A modular monolith is often an excellent starting point.

You can organize a React application into well-defined feature modules without immediately splitting it into independently deployed applications.

The goal isn’t to use the most advanced architecture. The goal is to choose the simplest architecture that meets the application’s actual needs.

Conclusion

Micro Frontend Architecture provides a way to divide a large frontend into smaller applications that can be developed and, in suitable architectures, deployed independently.

We’ve learned that:

  • Micro Frontends help organize large frontend applications around independently owned features.
  • The Host composes the overall experience.
  • Remote applications expose modules that other applications can consume.
  • Webpack Module Federation enables runtime sharing and loading of modules across separate builds.
  • exposes defines the modules a Remote makes available.
  • remotes tells the Host where to find other applications.
  • shared configures dependencies that can be reused across application boundaries.
  • Independent deployment requires stable contracts, compatibility, and reliable release processes.
  • Micro Frontends introduce trade-offs in performance, testing, state management, and operational complexity.

The key takeaway is simple:

Micro Frontends aren’t just about splitting frontend code into multiple projects. They’re about creating clear boundaries that allow teams to build and deliver frontend features more independently.

Before adopting this architecture, understand the problem you’re trying to solve. If a modular monolith is enough, keep things simple. If independent ownership and deployment are becoming genuine challenges, Micro Frontends may be worth exploring.

What’s Next?

Now that you understand the basics, consider exploring these topics next:

  • Module Federation in Depth: Understand runtime module loading, shared dependency negotiation, and version compatibility.
  • Micro Frontend Communication: Learn how independent applications exchange data using props, custom events, APIs, or shared state.
  • Micro Frontend Routing: Explore how the Host and Remote applications coordinate navigation.
  • Error Boundaries and Fallback UI: Handle failures when a Remote application cannot load.
  • Micro Frontend Performance: Learn about bundle splitting, caching, shared dependencies, and Core Web Vitals.
  • Micro Frontend vs Monolithic Frontend: Understand when each architecture makes sense.

Happy coding! ✌️

--

--