Micro Frontend Architecture:
An In-Depth Look And Application Examples

Furkan Emmezoğlu
12 min readApr 4, 2023

--

Turkish Version

Micro Frontend is an architectural approach that splits the application into separate projects.

With the Micro Frontend architecture, frontend projects can be developed independently as separate projects, which is different from the monolithic architecture where all algorithms are implemented in a single project.

Figure 1 — MF Architecture

To give an example (as shown in Figure 1), there are four different projects: shared, micro-app1, micro-app2, and micro-app3. These projects can be developed independently by entirely separate teams, using the most appropriate technology.

For example, the team working on micro-app1 in the figure preferred to use React, while the teams working on micro-app2 and micro-app3 continued with other frameworks such as Angular and Vue.

Basic Working Principle?

Figure 2 — Basic Working Principle

There are several approaches to implementing the Micro Frontend architecture including iFrame, Webpack Module Federation, and manifest.

Note: We will discuss these approaches in the following parts of the article.

The basic working principle for Micro Frontend is to design the main project that will call and display independently developed projects.

For example, when you enter a website that uses Micro Frontend architecture, the URL will direct you to the main project. The main project calls other apps using the applied method and shows them in the relevant sections. Thus, a whole system will be formed end-to-end.

What Is The Micro Frontend Advantages?

  1. Since you divide the project into small parts, it will be easier to scale each part.
  2. As each team focuses on one part of the job, the domain is better understood, and the focus is not distracted.
  3. As code fragments become smaller structures, a newcomer to the team can adapt to the code more quickly.
  4. Each team will be able to choose the most appropriate technologies for themselves. For example, team A can use Angular, while Team B can use React.
  5. A fault in one part of the project will not affect other parts.
  6. With progressive rendering, the speed of the application will increase.
  7. Teams can deploy independently, and CI/CD processes will be accelerated.
  8. Since each project will be tested internally, more comprehensive tests will be easier to write on smaller parts.

What Are The Micro Frontend Disadvantages And Troubles?

  1. CSS Conflicts

We should be careful when assigning names to class names in Micro Frontend architecture.

For example, both A and B teams use the same class name such as .container. When these teams' project renders in the main project, there will be a CSS conflict, and just one CSS becomes active. Result of this conflict, they can live with UI/UX problems.

To solve this problem, teams can use prefixes/suffixes with their classnames.

Another solution is teams can use styled-component/CSS-modules libraries. These libraries use a hash with classnames.

Note: CSS should be consistent in the main project. (e.g. same color)

2. Using different versions of the same libraries

Figure 3 — Versions of React

Teams designing Micro Frontend can use a different version of the same library. For example, Team A can work with React 18, while Team B can work with React 17. This is possible with Micro Frontend architecture.

This situation can be advantageous, but when downloading the same library two times in the client browser, the render time can be slower.

The problem can be solved by using package manager tools or by having the teams agree on a library version and use a shared library.

Another solution is with webpack. If we use webpack-modül-federation, we should do just simple configurations for the shared library. I will give details about this solution later in the article.

3. Communication With Other Project

When we describe Micro Frontend, we can say isolated teams theoretically. But when we implement this architecture, There may be dependencies between projects.

For example session data. Sessions should share between projects.

To solve this problem, these dependencies be managed with strong communication between teams.

3.1. Handle the Same API Call

If Both Project A and Project B call the same API endpoint, there may be a performance problem.

To solve this problem, we can request just a project then we can share this data with other Micro Frontend.

3.2. Common UI Components

If teams don’t determine common UI components, there may be unnecessary development effort and problems with duplicate code

To solve this problem, Teams should define common components and use them shared with structures such as storybooks.

3.3 Duplicate Code

Just like UI components, teams may develop similar algorithms in their codebase.

For instance, the time utility algorithm could be duplicated in multiple teams’ codebases, resulting in wasted efforts and duplicate code.

To solve this problem, teams should determine and manage such algorithms to avoid duplication.

Micro Frontend Architecture

Figure 4 — MF Architecture

Micro frontend architectures can be examined under five headings. Each heading has advantages and disadvantages.

The important thing when choosing an architecture is to select the one that suits the business model. When we look at the sector, we can see that Webpack Module Federation is widely used.

Another significant point here is the necessity of Micro frontend architecture.

Micro frontend architecture shows its advantages in systems formed by different domains.

If your system serves a single domain with a single team, you may be overengineering by applying a micro frontend.

  1. Server-Side Template

In this architecture, the server creates index.html and sends the client browser.

In index.html, there are other Micro Frontends HTML. These are added like plugs in.

These plugs can develop independently.

Pros

  • The most important advantage is that ready-made pages are sent to the browser in a compiled form. For this reason, loading time can be faster.
  • With the server-side template, you can also gain an advantage in SEO.

Cons

  • The complexity of the server-side architecture can be a disadvantage. As the system grows, it becomes hard to scale.
  • Server capacity and cost can also be a disadvantage.

2. Build-time Integration

It is the architecture created by packaging any Micro Frontend application and adding it to the main application like a 3rd party library.

For example, the Filtered Component, which is a part of the listing page, can be packaged and added to the main project.

{ 
"name": "@e-commerce/container",
"version": "1.0.0",
"description": "A product delivery app",
"dependencies": {
"@e-commerce/filter": "^1.2.3",
}
}

Pros

  • Build-time Integration is generally a simple and familiar way for Micro Frontend architecture.
  • The lazy load is supported so the application performance can be higher.

Cons

  • As a result of the changes made in the micro frontends, it is necessary to upgrade the version and deploy it in each project.
  • Version management also imposes a workload.

3. Runtime Integration with Iframe

Iframe is a DOM element that has been in use on browsers since ancient times.

In the main project, we can combine other Micro Frontends with Iframe. In other words, we can display independently deployed applications within the main project using Iframes.

<iframe src="https://www.trendyol.com" title="Trendyol"></iframe>

Pros

  • Iframe is generally a simple and familiar way for Micro Frontend architecture.
  • Isolating between projects is easy with Iframe.

Cons

  • Routing in Micro Frontends is the problem with Iframe.
  • We may see slower loading performance compared to other architectures.

4. Web Components Integration

Another way to set up architecture in run time is to add Micro Frontends to the main project with scripts.

Micro Frontends are rendered in the custom web component.

<script src="https://product.example.com/bundle.js"></script>
<script src="https://order.example.com/bundle.js"></script>
<script src="https://profile.example.com/bundle.js"></script>
<div id="micro-frontend-root"></div>
<script type="text/javascript">
// These element types are defined by the above scripts
const webComponentsByRoute = {
'/': 'micro-frontend-product-store',
'/order-food': 'micro-frontend-order-product', '/user-profile': 'micro-frontend-user-profile',
};
const webComponentType = webComponents ByRoute [window.location.pathname];
// Having determined the right web component custom element type,
// we now create an instance of it and attach it to the document
const root = document.getElementById('micro-frontend-root');
const webComponent = document.createElement(webComponentType);
root.appendChild (webComponent);

Pros

  • Supports independent deployment.
  • We can see fast loading performance by installing only bundles that are needed.

Cons

  • It shows slower loading performance compared to other architectures when all bundles are loaded at once.
  • More effort will be required to collect user analytics.

5. Webpack Federation

Before starting the Webpack Module Federation, it would be nice to know what is webpack.

Figure 5 — Webpack

Webpack is a module that builds your application with our dependent packages.

Webpack can be used with libraries like React, Angular, and Vue.

For instance, using Webpack, we can build a React application and obtain an index.html, index.js, and our dependent 3rd-party libraries. This allows us to quickly deploy our application with single-page application logic.

Webpack Configurations

While developing with Webpack, we can use default settings or create customized configurations.

Entry: It shows where the project should start to build the dependency graph. By default, src/index.js is set.

Output: It is the place where we specify the directory and name of the built file. By default, it creates a build file as ./dist/main.js.

Loaders: Webpack can only process JS and JSON files by default. If we want different types of builds such as Sass, and Text, we need to define them.

Mode: Although Webpack is built for production by default, it can make in development and none environments.

Plugins: In the Loader section, we mentioned that Webpack only builds JS and JSON by default. If we want to have HTML files as a result of the build, we need to define the relevant plugins in the configuration file. Another example of a plugin is the Webpack Module Federation plugin, which allows us to set up the Micro Frontend infrastructure.

// webpack.config.js
module.exports = {
entry: 'index.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'index_bundle.js'
},
module: {
rules: [ // Loaders
{ test: /\.txt$/, use: 'raw-loader' }
]
}
plugins: [new HtmlWebpackPlugin({template: './src/index.html'})]
};

Webpack Modüle Federation Plugin

With Webpack Modüle Federation Plugin, we can connect Micro Frontends applications.

Due to its ease of implementation, efficient sharing of dependencies, independent usage of the framework, and ability to solve routing problems with ease, it’s considered one of the best options for Micro Frontend architecture in the industry.

For this reason, in the continuation of the article, we can go into more detail by making an application where we can see the concept of Module federation.

Let’s consider a shell project that will combine Micro Frontends in the application, a search project that will enable users to search for a word from input, and a listing project that will display a list of items.

For this project, we can consider setting up two different teams where one team focuses only on the search domain and develops for this project, while the other team focuses solely on the listing domain.

Let’s start by creating React projects for our application.

Shell App: npx create-react-app shell
Search App: npx create-react-app search
List App: npx create-react-app list

After creating the applications, let’s add the dependencies that will be useful for module federation to our projects.

yarn add webpack webpack-cli webpack-server html-webpack-plugin css-loader style-loader babel-loader webpack-dev-server

Let’s create a file named bootstrap.js in all three projects to load Micro Frontends asynchronously.

Let’s copy the contents of the index.js file bootstrap.js and only import this file in index.js. This way, we can achieve asynchronous loading.

// bootstrap.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// index.js
import('../src/bootstrap')

At this point, our applications are ready to prepare their webpack configurations.

Let’s create files named webpack.config.js in the root folders of the applications for their webpack configurations.

// search-app/ webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const deps = require('./package.json').dependencies;
module.exports = {
/*Mode kısmı development veya production verilebilir.
Bu parametreye göre webpack build hassasiyetini ayarlayacaktır.*/
mode: 'development',
devServer: { // Application port
port: 3001,
},
/*Modül içerisinde js dosyalarını derlemesi için
babel ve css dosyaları için css-loader ve
style-loader modüllerini eklemeliyiz. */
module: {
rules: [
{
test: /\.js?$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
'@babel/preset-react',
],
},
},
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
],
},
/*Micro Frontend mimarimizi kurgulamayı
sağlayacak webpack modüle federation pluginide buraya eklenmeli.*/
plugins: [
new ModuleFederationPlugin(
{
name: 'SEARCH',
filename: 'remoteEntry.js',
exposes: { // export component
'./RemoteSelectorForInput': './src/components/Input'
},
shared: [
{
...deps,
react: { requiredVersion: deps.react, singleton: true, eager: true },
'react-dom': {
requiredVersion: deps['react-dom'],
singleton: true,
}
},
],
}
),
/*index.html çıksını alabilmek için HtmlWebpackPlugini eklemek gerekmektedir.*/
new HtmlWebpackPlugin({
template:
'./public/index.html',
}),
],
};

Up to this point, the same developments have been made in all three projects. From this point on, the module federation plugin will differ.

Now we can export the components from the search and list projects and display them in the Shell project.

  • name: This is used to select this plugin from other projects. For example, the Shell project will call this plugin as name@URL/fileName.
  • filename: This is the name given to the JS file generated by the build process that other projects need to find. It's a kind of entry point for the project. For example, name@URL/fileName.
  • exposes: This is where we define the components to be exported. In this JSON, the key is the remote selector, and the value is the path of the component to be exported. We can give any name we want to the key. The important thing is to use the same name when importing.
const Search = React.lazy(
() => import('SEARCH/RemoteSelectorForInput')
);
const List = React.lazy(
() => import('LIST/RemoteSelectorForList')
);

shared: This is where we define the dependencies that we will use in a shared manner. For more details, please refer to this link.

// list-app/ webpack.config.js
plugins: [
new ModuleFederationPlugin(
{
name: 'LIST', // remote selector name
filename: 'remoteEntry.js',// remote entry file
exposes: { // export component
'./RemoteSelectorForList': './src/components/list'// remoteKeyValue: local project path
},
remotes: {
// name@URL/fileName
SHELL: 'SHELL@http://localhost:3000/remoteEntry.js',
},
shared: [
{
...deps,
react: { requiredVersion: deps.react, singleton: true, eager: true},
'react-dom': {
requiredVersion: deps['react-dom'],
singleton: true,
},
},
],
}
),
  • In the module federation plugin that we will define in the Shell project, the difference is as follows:
  • remotes: This is where the exported Micro Frontends are defined. They should be defined in the format of name@URL/filename.
  • The import standard is as follows: import('name/exposesJsonKey').
// shell-app/ webpack.config.js
plugins: [
new ModuleFederationPlugin(
{
name: 'SHELL',
filename: 'remoteEntry.js',
remotes: {
// name@URL/fileName
SEARCH: 'SEARCH@http://localhost:3001/remoteEntry.js',
LIST: 'LIST@http://localhost:3003/remoteEntry.js'
},
exposes: {
'./Test': './src/App',
},
shared: [
{
...deps,
react: { requiredVersion: deps.react, singleton: true, eager: true },
'react-dom': {
requiredVersion: deps['react-dom'],
singleton: true,
}
},
],
}
),

Note: With the Federation method, a project can export a component and import from other projects at the same time. The Shell project has imported the search and list projects and exported the App component.

After configuring the Webpack.config.js files, we can start the projects with the following command:

webpack server

We should go to the localhost:3000 address from our browser to access the Shell app. Note that we defined the 3000 port in the config of the Shell project.

Shellapp

The areas marked in red in the Shell app are rendered from the Shell project, while the orange area is rendered from the search project and the green area is rendered from the list project.

Listapp

The list project is rendered on port 3003.

SearchApp

The search project is also rendered on its domain, on port 3001.

As a result,

Projects developed independently were merged and rendered at a single URL.

Each team was able to focus better on their work by developing their domain-specific features.

Since the project codes were smaller, projects were easily scalable, and testing costs were reduced.

React was used in the developed applications, but it was also possible to develop projects with different technologies.

Although there were no issues such as CSS conflicts, code duplication, or CSS incompatibilities in the projects at the end of the day, the likelihood of such problems increases as the domains within the Micro Frontend architecture grow in complexity and size.

In conclusion, while Micro Frontend architecture offers many conveniences, it also brings some challenges. It’s crucial to consider whether your project, team, and server structure can utilize the benefits of Micro Frontend. Answering these questions will help you successfully implement the Micro Frontend architecture in your project.

--

--