The Micro-Frontend Architecture Handbook

Over the years, in my role as a lead full-stack developer, solutions architect, and mentor, I’ve been immersed in the world of micro frontend architecture, working across different large-scale frontend projects where multiple teams, stacks, and deplo...

Jun 6, 2025 - 16:00
 0
The Micro-Frontend Architecture Handbook

Over the years, in my role as a lead full-stack developer, solutions architect, and mentor, I’ve been immersed in the world of micro frontend architecture, working across different large-scale frontend projects where multiple teams, stacks, and deployment pipelines had to coexist somehow.

As projects grew in complexity and teams worked in parallel across different stacks, it became clear that monolithic approaches couldn’t keep up. I needed practical tools that allowed easy cross-app interaction, independent deployability, better team autonomy, framework-agnosticism, and more. Some solutions worked elegantly in theory but struggled in real-world conditions. Others made things messier and more painful than helpful.

After diving deep into different paradigms—from iframes to Web Components, single-spa, Module Federation, Piral, Luigi, and hybrid setups—I even distilled my proven experience into a full-fledged online course on Udemy.

And today, in this comprehensive hands-on tutorial, I want to share my expertise and tell you more about micro-frontend architecture—method by method—with code, tradeoffs, visuals, and real-world insights.

Table of Contents

What are Micro Frontends For?

In traditional frontend development, we often build single, monolithic apps—one codebase, one repo, one deployment pipeline, one team. It works great for small to medium projects, sometimes even for larger ones.

Monolith App Diagram - Three Features in React

But challenges arise when:

  • Your frontend codebase expands beyond 50+ components.

  • Multiple development teams need autonomy over different parts and tech stacks.

  • Different sections require varying deployment frequencies (weekly or monthly).

  • You need to integrate diverse frameworks, like combining React features with an Angular-based CMS.

This is where micro frontends step in.

Micro frontends extend the principles of microservices to the frontend world. Instead of one big frontend app, you build independent frontend modules, each owned by a team, using its own tech stack, deployed separately, and integrated at runtime.

Micro-Frontends App Diagram - Three Apps in React, Angular, Vue

Think of it like Lego blocks:

  • Each block is similar to a self-contained micro frontend.

  • They plug into a shared layout or shell.

  • Each can evolve, update, or be replaced without affecting the others.

For example, imagine that you’re building a modern e-commerce site, and here’s what your business side expects from you:

SectionTeamStackDeployment
Product ListingSearch TeamReactWeekly
Product DetailsCatalog TeamAngularMonthly
Cart & CheckoutCheckout TeamVueBiweekly
CMS PagesMarketing TeamVanilla JSDaily

Each team wants autonomy, and with micro frontends, each of these sections becomes a separate app, loaded dynamically into a shell at runtime.

Why It’s Getting Popular?

Here are a few things everyone considers:

  1. Independent deployments – A little or no effort to coordinate every release.

  2. Team autonomy – Teams choose their own stack and tools on the project.

  3. Incremental upgrades – Migrate legacy apps piece by piece incrementally without the need to rewrite the whole app at once.

  4. Technical agnosticism – Vue, React, Angular? Doesn’t matter. They can all work together seamlessly at the same time in a single app.

  5. Better scalability – Parallelize work across teams to enable efficiency of delivery and scale at ease.

Now let’s discover how we can bring this idea to life in our projects.

Nowadays, there are different ways to achieve that, but not all solutions are equal. The implementation method you choose will drastically affect:

  • Developer experience

  • Bundle sizes and performance

  • SEO and accessibility

  • Runtime stability

  • Interoperability across stacks

So let’s begin by exploring the oldest, but still surprisingly viable method.

Method #1: Iframes & Cross-Window Messaging

You may ask, “Aren’t iframes bad?” They’re often misunderstood. While yes, iframes can feel clunky and isolated, they’re also the most secure and decoupled way to host micro frontends—especially when you don’t trust the team on the other side.

Micro-Frontend Method 1 - Iframes

What Is an IFRAME?

An iframe (inline frame) is an HTML element that allows you to embed another HTML page within your current webpage. The whole communication between apps is strictly based on events and delivered by means of the Post Message API.

If you need to send data to another app, you simply call the postMessage() method on that element. On the other side, to receive a message, you just have to subscribe to the message event. That’s it.

Real-World Example

Let’s see a simple example of two apps communicating with each other using iframes on two apps:

  • The Main Web App

  • A Search App.

Every iframe must be hosted somewhere to serve static content from it. It can be AWS Amplify, Digital Ocean, Heroku, GitHub Pages, or alike.

To help you out here, here’s an official GitHub guideline explaining how to host a website on their platform.

Let’s say you deployed a Search App on Github Pages and you were given this URL to host your app: https://example.github.io. Now let’s write some content for it.

Assuming that you want to post messages from the Search App to the Main Web App, and to subscribe to the incoming messages from it there. You can do it in this way:

console.log('Initializing Search App...');

// Subscribe to messages from outside the iframe (like Main Web App)
window.addEventListener('message', (event) => {
  if (event.data?.type === 'init') {
    console.log('Main Web App passed userId:', event.data.userId);
  }
});

// Simulate sending Search results back to Main Web App
window.parent.postMessage({
  type: 'searchResult',
  payload: ['Item A', 'Item B']
}, '*');

Here, you initialize the search app and set up two-way communication with a parent application (such as a main web app) using the Post Message API. You listen for incoming messages using the built-in message event. Once received, that message becomes available in the event.data object. Finally, you simulate sending data back to the parent by posting a searchResult message containing a list of items. This setup enables isolated iframe-based apps to communicate safely with the main shell application.

Then, in the DOM of the main web app, you need to include the iframe that will render the search app, specifying the URL to the hosted search app in this way:

<iframe
  id="search-mfe"
  src="https://example.github.io"
  style="width: 100%; height: 200px; border: none;"
>iframe>

Styles were added here to ensure that the iframe displays seamlessly within the layout for a cleaner UI integration.

And now you can pass some content from the main web app down to the search app and get some messages from it. You can accomplish it in the main web app’s JavaScript code in this way:

console.log('Initializing Main Web App...');

const iframe = document.getElementById('search-mfe');
iframe.onload = () => {
  // Send message to child iframe (inputs)
  iframe.contentWindow.postMessage({ type: 'init', userId: 42 }, '*');
};

window.addEventListener('message', (event) => {
  // Receive data from the Search App (outputs)
  if (event.data?.type === 'searchResult') {
    console.log('Received result from Search App: ', event.data.payload);
  }
});

As you see, when the iframe loads, the init event is sent to the search app (the type can be anything you want, just ensure it matches the one that another app expects from you). And then, in the message event handler as before, you can receive the incoming messages from the search app, and do something with them.

Here are a few pros and cons to consider, along with popular use cases:

✅ Pros:

  • Strong sandboxing: No shared memory, no shared styles.

  • Zero dependency clashes: One iframe is equivalent to one environment.

  • Perfect for legacy: Easy to wrap old apps in an iframe.

  • Practical for micro-apps in PHP, Java, Razor (ASP.NET)

❌ Cons:

  • Slow rendering

  • Difficult shared navigation

  • Inconsistent/complicated styling

  • Complex communication

  • Must be hosted somewhere