AvnishYadav
WorkProjectsBlogsNewsletterSupportAbout
Work With Me

Avnish Yadav

Engineer. Automate. Build. Scale.

Ā© 2026 Avnish Yadav. All rights reserved.

The Automation Update

AI agents, automation, and micro-SaaS. Weekly.

Explore

  • Home
  • Projects
  • Blogs
  • Newsletter Archive
  • About
  • Contact
  • Support

Legal

  • Privacy Policy

Connect

LinkedInGitHubInstagramYouTube
Introduction to Micro Frontends: Building Scalable Architectures
2026-02-21

Introduction to Micro Frontends: Building Scalable Architectures

8 min readShipEngineeringSystem DesignReactArchitectureWebpackMicro FrontendsModule Federation

A deep dive into decoupling frontend monoliths. We explore integration strategies, Webpack Module Federation, communication patterns, and the engineering trade-offs of distributed UI systems.

There comes a specific breaking point in the lifecycle of every successful product. You start with a monolithic React or Vue app. It’s fast, easy to debug, and everything lives in one repo. Life is good.

Then the team grows. Suddenly, you have 30 developers pushing to the same repository. The CI/CD pipeline takes 45 minutes to build. Merge conflicts are a daily ritual. One bad commit in the billing dashboard brings down the user settings page.

This is the Frontend Monolith problem.

Backend engineering solved this years ago with Microservices. We learned that decoupling services allows teams to move fast and break only their own things. Micro Frontends bring that same architectural rigor to the browser. In this post, we’re going to look at what they are, how to architect them using modern tools like Module Federation, and most importantly, the trade-offs involved.

Defining the Architecture

A Micro Frontend architecture splits a web application into vertical slices. Instead of thinking about the application as "The Frontend," you think of it as a composition of features owned by independent teams.

Each distinct part of the application (e.g., Search, Product Details, Checkout) becomes a self-contained application. It has its own repository, its own build pipeline, and its own database slice (ideally).

The Core Principles

  • Technology Agnostic: Theoretically, Team A uses React and Team B uses Vue. (Note: In practice, I rarely recommend this unless you are migrating legacy code. Sharing a framework reduces bundle size significantly.)
  • Isolated Code: Never share runtime state unless absolutely necessary. No global variables.
  • Resilient: If the "Related Products" widget crashes, the rest of the page should remain interactive.

Integration Approaches

The hardest part of this architecture is figuring out how to stitch these fragments together into a cohesive user experience. There are three main approaches used in production today.

1. Build-Time Integration (NPM Packages)

You publish each micro frontend as a private NPM package and install them into a "Container" app.

The Verdict: Avoid this. It solves the code organization problem, but not the deployment problem. If you update the Checkout package, you still have to rebuild and redeploy the Container app to see the change. It effectively remains a monolith in terms of deployment velocity.

2. Server-Side Composition

Using technologies like Server-Side Includes (SSI) or newer tools like Tailor, the server assembles the HTML fragments before sending the page to the browser.

The Verdict: Great for SEO and First Contentful Paint (FCP), but it complicates local development and requires a robust infrastructure layer. It forces your frontend teams to think too much about server configuration.

3. Run-Time Integration (The Standard)

The Container application loads the micro frontends dynamically in the browser via JavaScript. The container is essentially a skeleton that fetches the logic it needs on demand.

This is where Webpack 5 Module Federation changed the game.

The Game Changer: Module Federation

Before Webpack 5, runtime integration was a hacky mess of iframes or global window injection. Module Federation allows a JavaScript application to dynamically load code from another application—at runtime—as if it were a local module.

Here is a simplified look at how you configure a Host (the container) and a Remote (the micro frontend).

The Remote Configuration (e.g., Checkout)

This app exposes its components to the world.

// webpack.config.js for Checkout App
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: "checkout",
      filename: "remoteEntry.js",
      exposes: {
        "./BuyButton": "./src/components/BuyButton",
        "./Cart": "./src/components/CartPage",
      },
      shared: { react: { singleton: true }, "react-dom": { singleton: true } },
    }),
  ],
};

The Host Configuration (e.g., Main App)

This app consumes the remote components.

// webpack.config.js for Main Container
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: "container",
      remotes: {
        checkout: "checkout@http://localhost:3001/remoteEntry.js",
      },
      shared: { react: { singleton: true }, "react-dom": { singleton: true } },
    }),
  ],
};

Inside your container's React code, you simply lazy load the component:

const BuyButton = React.lazy(() => import("checkout/BuyButton"));

This pattern allows the Checkout team to deploy a new version of their remoteEntry.js without the Container team needing to redeploy a single line of code. The next time a user refreshes the browser, they get the new code.

Communication Between Frontends

Once you split your app, you lose the ability to just pass props down a giant tree. How does the Header (owned by Team A) know that the user added an item to the Cart (owned by Team B)?

We need loose coupling. Here are the reliable patterns:

  1. Custom Browser Events: Use the native DOM event system. The Cart dispatches new CustomEvent('cart:update', { detail: count }) and the Header listens for it. This is zero-dependency and framework agnostic.
  2. Pub/Sub System: A lightweight external library for publishing and subscribing to topics.
  3. URL State: The source of truth for navigation should always be the URL. If one MFE changes the route, the others react to that change.

Anti-Pattern Warning: Do not try to share a complex state management store (like a single Redux store) across micro frontends. That creates the coupling you are trying to avoid.

The Trade-Offs (Read Before Implementing)

As builders, we know there are no solutions, only trade-offs. Micro Frontends are complex.

1. Payload Size

Even with shared dependencies, you will likely download more JavaScript than a properly code-split monolith. You have duplication of utility libraries and initialization logic.

2. Styling Consistency

CSS is global by default. If Team A writes h1 { color: blue }, Team B is in trouble. You must use CSS Modules, Styled Components, or Shadow DOM to ensure strict style isolation.

3. Operational Complexity

You are moving complexity from code to infrastructure. You now have 10 pipelines to maintain instead of one. You need orchestration to handle version mismatching.

When to Use Micro Frontends

Do not use this architecture for your personal blog or a startup MVP.

Micro Frontends are the solution to an organizational problem, not a technical one.

You should implement this when:

  • You have multiple teams (20+ frontend engineers) working on the same product.
  • Deployment delays are causing measurable business friction.
  • Different parts of the app have vastly different scalability requirements.

Conclusion

Micro Frontends allow large organizations to scale their frontend development by decoupling teams and codebases. With tools like Module Federation, the implementation has become significantly cleaner, allowing for seamless runtime integration.

However, complexity is conserved. If you don't have the scale to justify the infrastructure overhead, stick to a modular monolith. But if you are hitting the wall, it’s time to break it apart.

Share

Comments

Loading comments...

Add a comment

By posting a comment, you’ll be subscribed to the newsletter. You can unsubscribe anytime.

0/2000