Part of the Orange Group

Expert's Voice
18 min read

Micro Frontends – The Missing Piece Of The Puzzle In Feature Teams

Article written by:

~Micro Frontends~ – The Missing Piece Of The Puzzle In Feature Teams

Increasing scalability and agility rarely ceases to be top of mind in any legitimate frontend development team. When working on large, complex products, how can you ensure rapid, frequent delivery of features that consist of both backend and frontend? Breaking up frontend monoliths into many smaller pieces as it is done for the backend seems to be the answer. When properly executed, micro frontends can improve the effectiveness and efficiency of your team. Here’s how.

The idea behind Micro Frontends is to think about a website or web app as a composition of features which are owned by independent teams. Each team has a distinct area of business or mission it cares about and specialises in. A team is cross functional and develops its features end-to-end, from database to user interface.

Decoupling bigger problems into smaller ones to increase agility, reusability, and scalability has always been one of the Holy Grails of IT and the progress made in this area over the last two decades has been staggering. But still, new limitations of working with larger and larger codebases keep arising. The biggest minds in the industry have been breaking their heads over this challenge, trying to address the problem both from a technology and organizational perspective. To date, the microservices architecture is definitely still closest to solving it.  

In this article you will learn:

  • How the microservices architecture and micro frontends function; 
  • What their biggest advantages are;
  • Which requirements must be met when implementing micro frontends;
  • Which techniques or approaches you can use;
  • And how each of these solutions compare to one another.

Microservices architecture and micro frontends – what they solve and how they function

The microservices architecture promises:

  • Easier to build and maintain applications
  • Improved team productivity
  • Flexibility in using technologies 
  • Scalability

The solution is to split large systems into fine-grained, loosely coupled services organized around well-defined business capability. Each service is then small enough to be developed by one development team and can be rapidly adjusted to new business needs and deployed without the risk of breaking other parts of the system.

The peril of this approach is that the system will be broken up into many unrelated applications. Although this is great for developers to handle, it is not what a user expects of a system; most people wouldn’t like to work with numerous small applications to do their job. Therefore, what has been decomposed for this process has to be put back together in the user interface. 

Putting the pieces back together

The common approach is building a monolithic frontend layer between users and microservices. For every non-trivial system, a huge frontend codebase threatens the benefits of the microservices architecture. And this is where micro frontends come in.

The basic idea of micro frontends is pretty straightforward – composing the user interface of multiple fine-grained parts organized around a well-defined business capability. Each part is then small enough to be developed and owned by one vertically structured team. The team who owns both frontend and backend establishes a truly self-sufficient feature team.


Unfortunately, however, micro frontends cause the same challenges that make microservices so hard to implement. Moreover, any shortcut could potentially affect the user experience in a negative way. Consistency, security, interoperability, performance, scalability, and ownership must be secured to assure a seamless user interface.

Although different micro frontend approaches tackle various problems, none has managed to cover them all yet. But before we dive into the details of these different approaches, let’s take a closer look at the key motives for choosing micro frontends in the first place and the pros and cons compared to the monolithic approach.


Micro Frontend Architecture


The 3 biggest advantages of micro frontends explained

Advantage #1 of micro frontends: a scalable team setup

3 different frontend approaches and their consequences for team organization

There are many good reasons to opt for the micro frontend approach but one of the most important, if not the most, comes from a scalable team setup. 

At least three possible frontend architectures can be identified that influence the organization of development teams: monolithic frontend and backend, monolithic frontend with backend microservices, and micro frontends. Below, we describe the consequences regarding team organization for each one.

Monolithic frontend and backend

The common approach for building solutions requiring both frontend and backend is to split the project horizontally and communicate between these layers through REST APIs. 

If the system is small enough for one team to develop, the best option is to keep your architecture simple. You can do this by creating a Single Page Application (SPA) frontend and connecting it to the backend through REST APIs. Then adjust your team setup based on the workload needed in each layer. 

Good practice is to ensure that from the very beginning your code is well-structured into components and when your solution grows, you can introduce  another team or two without re-architecting it. 

The limitations of this architecture are obvious though: the more teams modify the codebase, the more coordination and integration is needed which paralyzes product development.

micro frontends

Monolithic frontend with backend microservices

Especially in large enterprises, the need for additional development teams usually starts at the backend. Most systems need a lot of business logic. They require processing that cannot be done on the browser side, for instance, or complex integrations with the legacy system. One cross-functional team is no longer enough in these instances. 

The first step most companies take to scale their architecture is to split the backend codebase vertically to tackle complexity. The frontend is assigned to a specialized frontend team while the rest of the work is distributed among various backend teams. Backlog items are consequently broken down into chunks and are to be delivered by separate teams. These teams then work out dependencies either through contract negotiation or – in a more agile approach – through big backlog planning.


The biggest challenge that arises with this approach, is managing these dependencies and synchronizing releases. Testing also becomes cumbersome, as you need to wait for all the separate pieces to arrive in the test environment before you can verify it. 

It becomes even more complicated when the solution has to embed features from different product backlogs (which quite often is the case). 

At larger scale, this type of approach requires a lot of management. Daily deployments are pretty much out of the question. This is why developers and architects working in large enterprises with complex frontends sought for a solution for ultimate vertical scaling, adding frontends to their already game-changing microservice architecture – micro frontends.

Micro frontends

In order to develop, test and release its features fast, a team needs to be able to work without dependencies  with other teams. The same promises of microservices at the backend can be fulfilled in the user interface field by micro frontends and the same principles that enable independent teamwork can be applied. 

In this setup, both areas – frontend and backend – are tightly coupled as the requirements are sourced from one product backlog. Once again, one team can deliver the entire feature in a simple architecture. When executed properly, this happens without compromising the user experience.

micro frontends

In order to execute it well, micro frontends bring a lot of similar issues known with backend microservices that must be solved. Otherwise, the user might still perceive or experience the system as a patchwork of divergent features.

Advantage #2 of micro frontends: freedom of choice on technology

Apart from creating a scalable and independent team setup, the micro frontend approach also helps to deal with the amount of technologies applied to the frontend. Every quarter comes with new ideas about how to develop user-facing parts of systems. Quite often, new versions of frameworks are not even backward compatible, which means that each version actually stands as a separate framework.

micro frontends

One of the advantages of the microservices architecture is freedom of choice on technology used. The same freedom can be given to frontend developers – at least to some extent – when the user interface is splitted into independent modules. 

Advantage #3 of micro frontends: resilience

The real costs of any system are not well represented by the initial development costs of a codebase but by the maintenance of it. The purpose of the endless spiral of code refactoring and system rearchitecting is to keep introducing functional changes at the same pace as at the beginning. 

Micro frontend architecture makes the code base more resilient by introducing constraints that:

  • isolate the impact of changes;
  • prevent code coupling;
  • and preserve the architecture over time.

These constraints are preventing uncontrolled dependencies, limiting code reuse, and enforcing service boundaries.

No more uncontrolled dependencies

Large applications developed over years inevitably become full of code dependencies that are hard to track and maintain. Developers working under pressure of time to market or just trying to optimize how they work puts a lot of uncontrolled dependencies between different parts of code. When the new dependency is introduced it always seems to be a good idea to reuse some business logic, cached data or resources pool. What happens later are unforeseen consequences of changes in such shared functions. 

micro frontends

By splitting the code base into several disconnected parts it is easy to avoid such dependencies. There is no possibility in one micro frontend to reuse existing features from other micro frontends because of the natural boundaries in between. Hence, any unintended side effects of changes are limited to only one micro frontend.

Limited code reuse

The purpose of the commonly followed principle Don’t Repeat Yourself (DRY) is to limit the size of your codebase and in consequence decrease the probability of errors. On the other hand each abstraction developed into code introduces dependency. Over time it often also appears that abstractions must be adjusted to the specific context of usage. When your codebase of micro frontends is limited to only a few features, it is less probable that developers are tempted to create such abstractions. Instead, when they find some ocasion to reuse code they just copy and paste the relevant fragments, which is often much better than introducing dependencies.

Service boundary enforcement

The architecture of the system is usually at the effect of some analysis and design decisions. However, deciding on something and complying with those decisions is often not the same. Right after services are split and responsibilities are agreed upon and documented, a drift begins. Lack of clear boundaries or the ability to cross boundaries can cause and allow developers to break the previously agreed upon design. There are tools, like code review or dependency checks implemented in CI/CD pipelines, but with separate micro frontends it is impossible to drift from the agreed architecture. 

6 common requirements for micro frontends

In order not to lose any of the potential benefits of micro frontends, the practical implementation of this architecture must meet some common requirements. The following six are important not to overlook:

  1. Independant deployments – the greatest risk of using micro frontends is that when they are not implemented properly, they will require deployment coordination. Besides good design that encapsulates meaningful features in a single component and assures backward compatibility at all times, the components themselves must be deployable one by one without the need for any coordination.
  2. Hot deployments – the team who develops certain application fragments must be able to deploy new versions without causing any downtime. Using strategies such as rolling updates or canary deployments must be considered. Use of advanced HTTP routing mechanisms with a deliberate path system can help a lot.
  3. Unified styles / components – having the application built as a patchwork of incompatible tiles could have a devastating effect on the user experience. The question of how visual and behavioral consistency will be assured must be addressed from the very beginning of the micro frontend introduction. The solution often covers both technical (shared UX libraries) and organizational aspects (shared UX team).
  4. Authentication and authorization – obviously a user must authenticate only once. Authorization context and rules must be shared by all components both in the frontend and the backend.
  5. Cross-component communication – even if communication between components introduces coupling and should be avoided because of that, it is hard to imagine an application consisting of completely detached pieces. Particular micro frontends must be able to share application context (i.e. user or other resources identification) and to notify each other about the change of internal states. The selected communication method should prefer indirect communication based on events or address bar over a direct use of other components API.
  6. Search engine optimization – the severity of this need depends on the specific use case but for some applications it is the first class citizen to be solved.

Micro Frontend Architecture


Micro frontend techniques – 3 approaches to decoupled frontends

Micro frontend techniques can be grouped into three categories and each of those techniques has some pros and cons. We can differentiate between:

  1. Build-time integration – the application is  produced as one bundle and than delivered at once to a user
  2. Server-side integration – the application is integrated while pages are served, user browser gets full application
  3. Client-side integration – the application is delivered to user web browser in parts and than is integrated in the browser

 Below we take a closer look at each of these three approaches by using an example of a fitness tracking application like Strava or Endomondo. Each of these applications have similar features and functions, like a dashboard presenting the athlete’s profile summary, their latest activities, some ongoing challenges, etc.

Build-time integration

The first approach to decoupled frontends is organizing the codebase in independent repositories. With build-time integration, each micro frontend is built and published as an independent package. The complete application imports those packages and composes the user interface from contained components. 

In this way, with a little effort on organizing teams and on the proper split of features among them, reasonable team independence can be achieved. Although it will not deliver all possible benefits of micro frontends, such as technology diversity or deployment autonomy, a strong point is that it does not require any additional tools other than the ones that already used by developers. 

In comparison to other approaches, build-time integration simplifies consistency assurance. It is also the easiest and most effective way to reduce the amount of data transferred to the user browser as the whole application bundle is optimized during the build phase. 

micro frontends

What needs to be considered when designing the fitness tracking application in our example, is use of indirect communication between components which will decrease coupling.

Server-side integration

The general idea of the second approach is depicted in the diagram below. 

micro frontends

The browser request for a page (1) from the “Layout service” which first requests “Page templates service” for the page layout (2). The layout contains HTML compatible tags with the URLs of page fragments to be included (3). The “Layout service” requests content of all included parts calling services that implement specific features. More advanced implementations of the layout service executes queries in parallel (4) with support for failover and fast response streaming. 

After all parts are retrieved (5), the full content of the requested page is prepared and returned to a browser (6).

The layout service plays a central role to establish consistent behavior of the application from user perspective yet it is made up of independently developed and deployed parts. 

Server-side integration is extremely useful when the application contains pages composed of a number of independent tails, some user specific and some shared between users like e-commerce sites usually have. 

There are plenty of technologies you can use that apply this pattern, like Server Side Includes, Edge Side Includes, and Zalando’s project Mosaic with “fragment” tags. 

Server Side Includes

Server Side Includes (SSI) is a scripting language interpreted by a web server used for including the content of one or more files into a web page. The language syntax is based on directives placed in HTML comments which are processed by an SSI enabled web server.

<!--#directive parameter=value parameter=value -->

The most commonly used directive is ‘include’ allowing inclusion of a document into another one. The included document can be referenced by a path relative to the current file directory. For example:

<!--#include file="footer.html" -->

A more powerful and recommended option is the use of a virtual parameter which can specify any resource  including CGI scripts or remote pages:

<!--# include virtual="/athlete/768769879/details" -->

The language also offers variables and conditional clauses to create more context aware content on the pages.

SSI is  supported by popular web servers like Apache or NGINX.

Edge Side Includes

Edge Side Includes (ESI) is a markup language for edge level dynamic web content assembly. The goal of ESI is to solve the problem of web infrastructure scaling and content publishing. Similarly to SSI it offers transclusion by an esi:include tag:

<esi:include src="" />

Conditional execution commands combined with a vast number of predefined variables allows to implement truly dynamic content on pages served to a user.

    <esi:when test="$(HTTP_COOKIE{group})=='Advanced'">
        <esi:include src=""/>
    <esi:when test="$(HTTP_COOKIE{group})=='Basic User'">
        <esi:include src=""/>
        <esi:include src=""/>

ESI is supported by caching proxy servers such as Varnish or Squid.

Zalando’s project Mosaic

Built in Zalando, Mosaic is one of the first mature, build for purpose, frameworks for micro frontends that implements server-side integration.  

The central point of Mosaic architecture is “Tailor,” the implementation of the layout service in this server-side micro frontends architecture. In order to include micro frontends in a page a “fragment” tag is used:

    <script type="fragment" src=""></script>
    <fragment src=""></fragment>
    <fragment src="" primary></fragment>
    <fragment src="" async></fragment>

 When compared to plain SSI or ESI tags the fragment tag offers additional useful attributes:

  • primary denotes a fragment that sets the response code of the page
  • timeout – optional timeout of fragment in milliseconds (default is 3000)
  • async – postpones the fragment until the end of body tag
  • public – prevents Tailor from forwarding filtered request headers from upstream to the fragments
  • fallback-src – URL of the fallback fragment in case of timeout/error on the current fragment

As mentioned above, Mosaic is built to serve micro frontends and offers features benefits such as:

  • Composing pre-rendered markup on the backend. This is important for SEO and fastens the initial render.
  • Ensuring a fast Time to First Byte. Tailor requests fragments in parallel and streams them as soon as possible, without blocking the rest of the page.
  • Enforcing performance budget. This is quite challenging in other setups, because there is no single point where you can control performance.
  • Fault Tolerance: rendering the meaningful output, even if a page fragment has failed or timed out.

Page templates can be served simply from the file system or from dedicated services if more sophisticated template management is required.

The second part of Mosaic is Skipper. With a companion of Innkeeper, Skipper establishes an advanced HTTP router which can be used whenever there is a need to hide the complex world of microservices. The Skipper itself offers rule-based HTTP request routing with filtering and enrichment. Innkeeper serves as an API for run-time Skipper’s rules management.

Server-side integration allows easy composing applications from micro frontends but it does not solve challenges that arise when a truly rich fronted application is required.

Client-side integration

Last but not least, the client-side integration approach. Here, the construction of micro frontends is integrating the application in the user web browser. Each part of the application is delivered to the browser independently and then the application is glued while being rendered. 

With this approach no additional infrastructure is needed to construct the application in run-time and it seems to be the most flexible. The application components can share some user context and therefore act like the one integrated during build-time without compromising other requirements for micro frontends. 

micro frontends


Iframes is an old client-side integration technique which can be used to embed one HTML document into another. In the context of micro frontends the solution lies in embedding every micro frontend application page layout using iframe tags with an src attribute pointing to the URL where the application is served. 

Similar to SSI/ESI in this approach every micro frontend can be hosted on different addresses. Opposite to SSI/ESI the client browser is responsible for downloading each piece independently and displaying the complete page.

     <iframe src="" style="border: none;"/>
     <iframe src="" style="border: none;"/>
     <iframe src="" style="border: none;"/>

To be a fully-fledged micro frontend, an application embedded in iframe should be able to communicate with its parent. This can be achieved by using the window.postMessage() method but in most cases, it’s good practice to wrap such a low level API with a library that eases integration from the perspective of micro frontends. 

Besides standard use cases covering data exchange between micro frontends that affect the state of rendered content, it is also required to enable communication between parent and micro frontend. The latter ensures that the size of the iframe fits the size of the micro frontend content. When iframe content overflows, information about the real size of embedded content has to be propagated to the parent application and the iframe height has to be adjusted by the parent.  

Iframe based integration works best when it’s required from the micro frontend platform itself to assure the highest level of isolation between the parent application and the micro frontends. In such a scenario it is possible to create micro frontends using any technology or framework, including easy legacy applications integration which is unique in client-side integration. 

Deployment of micro frontends also doesn’t require any special way of building or packaging the source code. 

The iframe approach ensures that deployment of a new version of micro frontends won’t affect others that have already been deployed either. This technology freedom guarantees that the whole system won’t get stuck in some framework as no compatibility of micro frontends is required. This way a technical debt can be paid according to the actual business priorities of each development team.

This high isolation eases integration but at the same time it causes some UX limitations that should be taken into consideration when considering your integration solution. 

Iframes are definitely not the best option when your main focus lies on UX design. It is possible to provide a good UX design (also in case of responsive web design) but it’s a little bit more complicated than with other approaches. The main limitation is caused by the fact that micro frontend content cannot exceed iframe boundaries. Popup windows, for example, that are displayed over several iframes cannot be displayed properly. 

The other factor that needs to be taken into consideration is the overhead of resources downloaded to the browser. Each resource (css, js, etc. ) required by a specific micro frontend must be downloaded separately. Although it might not be a problem for the majority of workstations used by clients nowadays, be aware that it is not possible to load only one instance of frontend framework core libraries into memory.

Single SPA

Single SPA is a JavaScript framework designed to build a user interface composed of multiple single-page applications which promises coexistence of many frameworks. Even different versions of the same framework can be mixed in one page. 

When using Single SPA, each micro frontend can be deployed independently. Another nice feature is lazy loading of the code. A specific micro frontend bundle is loaded only when it’s needed, which improves the load speed of the application. 

The architecture of any Single SPA application consists of two concepts:

  1. Applications – the micro frontends themselves. Each application can respond to URL routing events and must know how to bootstrap, mount, and unmount themselves from the DOM. The main difference between a traditional SPA and Single SPA applications is that the latter must be able to coexist with other applications and do not each have their own HTML page.
  2. Single SPA config –  the HTML page and the JavaScript that registers applications with Single SPA.

Having micro frontend embeddable in Single SPA does not require huge adjustments of the frontends. A new micro frontend declaration requires implementing Single SPA lifecycle functions and exposing a file with these implementations for the main application. 

Single SPA lifecycle functions are very similar to React component lifecycle functions – an object props has the property domElementGetter, returning the DOM element in which a micro frontend should be placed or removed.

  • bootstrap – will be executed once, right before a micro frontend is mounted for the first time.
  • mount – will be executed when an activation function condition is met (i.e. a proper URL) or a micro frontend is mounted manually.
  • unmount – will be executed when an activation function condition is not met (i.e. an improper URL) or a micro frontend is unmounted manually.
How to mark a frontend-application as Single SPA micro frontend in the code

The first step if you want to mark a frontend-application as Single SPA, is to prepare a main micro frontend file and implement lifecycle methods. This can be done manually but the framework provides a convenient helper to do it.

import React from 'react'
import ReactDOM from 'react-dom'
import singleSpaReact from 'single-spa-react'
import root from './root.component.js'

const reactLifecycles = singleSpaReact({
   rootComponent: root

export function bootstrap(props) {
    return reactLifecycles.bootstrap(props);

export function mount(props) {
    return reactLifecycles.mount(props);

export function unmount(props) {
    return reactLifecycles.unmount(props);

In the second step, you can use Webpack to bundle your application into one bundle file, athletes.bundle.js for example, and expose it from any server that is accessible on client-side. 

How to use Single SPA micro frontends in the frontend application

The application concept = Single SPA as a framework

The first step is to register a micro frontend in the root application. For this, you should use the function below:

    appName: string,
    applicationOrLoadingFn: () => <Function | Promise>,
    activityFn: (location) => boolean,
    customProps?: Object
  • appName – a name that will be used in the single-spa as the identifier
  • applicationOrLoadingFn – a function that can return an application or an application promise
  • activityFn – a function that should return true when an application has to be mounted and false when the application has to be unmounted. 
  • customProps – an optional object that will be passed into life cycle methods whenever they are called. 

The last step is to start() the Single SPA engine and then you’re all set.

import {registerApplication, start} from 'single-spa';

const domElementGetter = () => document.getElementById('micro-font-end-container');
const pathPrefix = (prefix) => {
   return (location) => location.pathname.startsWith(`${prefix}`);

registerApplication('athletes', () => import ('athletes.bundle.js'), pathPrefix('/athletes'), {domElementGetter});
registerApplication('challenges', () => import ('challenges.bundle.js'), pathPrefix('/challenges'), {domElementGetter});
   <script src="/dist/root.js"></script>
   <a onclick="singleSpaNavigate('/athletes')">Go to athletes</a>
   <a onclick="singleSpaNavigate('/challenges)">Go to challenges</a> 
   <div id="micro-font-end-container">
       //i.e. here micro frontends can be injected


The parcel concept = Single SPA as a library

When using Single SPA as a framework, the container application is a simple container of applications that are switched in reaction to root changes. Another option is the parcel concept. Here, you create a container application in any framework as a base of the system and a parcel (or actually micro frontend) has to be directly mounted in a particular place. That way one page can contain multiple micro frontends. This is much closer to the concept of building the user interface as a composition of decoupled features, yet visible and accessible at the same time. 

The parcel should be unmounted at the right time too. 

In the example below, React is used as the main framework so componentDidMount and componentWillUnmount can be used for mounting and unmounting parcels.

class AthletesMicrofrontendComponent extends React.Component {

    componentDidMount() {
            .then(athletesApp => {
               const domElement = document.getElementById('athelete-micro-frontend-id');
               mountParcel(athletesApp, {domElement})

       return <div id="athelete-micro-frontend-id"></div>


Comparison of micro frontend solutions

The table below summarizes and highlights the key strengths and weaknesses of each approach:

TechniqueCategoryExample implementationStrengthsWeaknessesRecommendation
Build-time packagesBuild-time integrationNPN, yarnEasy to setup – no additional tools needed

Known to developers

Code deduplication

Easy to achieve UX consistency

Lockstep release process – the whole bundle must be recompiled and deployed

Limited to one framework 

Use only for small projects (no more than three teams) only if other options seems to be to much complicated
SSI/ESIServer-side integrationSSI – NGINX, Apache Http

ESI – Varnish, Squid

Easy to setup – HTTP servers and cashes are part of every web application architecture

Full independency of deployments 

Lack of cross-component communication – it must be solved otherway

Hard to achieve consistent UX look & feel

Lack of build in authentication and authorization

Use for eCommerce like web applications when microservices with less fluctuating application and services architecture
Specialized micro frontends middlewareServer-side integrationZalando’s Mosaic, 

Compoxure, micro-fe

Full independency of deployments

Excellent control over page performance and failover

Built-in advanced dynamic routing with autodiscovery

Hard to setup – requires additional components

Lack of cross-component communication – it must be solved otherway

Hard to achieve consistent UX look & feel

Lack of build in authentication and authorization

Use for eCommerce like web applications when microservices with high fluctuation of application and services architecture
IframesClient-side integrationEasy to setup – no additional tools needed

Full independency of deployments

High component isolation

Cross-component communication

Real framework agnostic  

Hard to implement deep-linking

Hard to implement RWD

High resources consumption 

Use when framework agnostic and component isolation is the key concern and UX is not so important
Micro frontend frameworkClient-side integrationSingle SPAFull integration between micro frontends and container application

Support for all major JS frameworks

Lazy loading

Not well documented engineUse in rich application composed of many communicated micro frontends


If you want to get the most out of your microservices architecture, the micro frontend pattern really seems to be the missing piece of the puzzle. Depending on your needs, the exact solution may vary though. For an eCommerce site for instance, you can select any type of server-side integration. When an advanced reach application is your target, however, one of the client-side techniques will be a better solution for you. 

If micro frontends would still introduce too much hassle in your opinion, at least opt for build-time modularization. It will always pay off in the long run. 

In case you have any experience with micro frontends and their implementation yourself, please share below. We would gladly receive your thoughts and concerns and further discuss the subject.


Micro Frontend Architecture


What Can We Do For Your Business?

Contact Us!

You might also be interested in