Introduction to Micro-Frontends

Ahmet Önol
Insider Engineering
6 min readJul 25, 2022

Is it possible to divide a frontend application into small pieces and enable more effective development?

Techniques, strategies and recipes for building a modern web application with multiple teams that can deliver features independently.

This article provides information about micro frontend approaches and their implementations.

What are Microservices?

Microservice (also known as the microservice architecture) is an architectural style that structures an application as a collection of services that are

  • Highly maintainable and testable
  • Loosely coupled
  • Independently deployable
  • Organized around business capabilities
  • Owned by a small team

The microservice architecture enables the rapid, frequent, and reliable delivery of large and complex applications. It also enables an organization to evolve its technology stack.

What are Micro-Frontends?

The term Micro Frontends first came up at the end of 2016. The current trend is to build feature-rich and powerful frontend applications which sit on the top of a microservice architecture. It has a lot in common with the Self-contained Systems concept, but Micro-Frontends is clearly a more friendly and less bulky term.

Micro frontends give you the ability to achieve a less complex and cumbersome architecture. In particular, thanks to the micro frontend approach, you can split an entire application into small pieces, independent parts. Each of them will then be implementable by different frontend teams, and even with different technologies. This ensures the same scalability, flexibility, and adaptability that comes with the backend microservice architecture.

What are the Major Problems?

The frontend layer that is often developed by different teams, grows over time and becomes difficult to maintain. While the backend architecture is usually designed to be modular these days, the frontend is still developed in a single codebase, it’s called Frontend Monolith.

Frontend monolith

Disadvantages of Monolithic Applications

  • Codebase becomes very big, hard to maintain, and riddled with unwanted complexity.
  • Deployments become very long and involve a lot of moving parts that most developers don’t know how to deal with when issues arise.
  • Deployments can contain too many changes. Irrelevant changes can prevent developers from deploying or forcing them to revert the versions.
  • Production builds and deployments took too much time.
  • Multiple teams can make changes in the same codebase which cause code conflicts.

and the list goes on.

This is why it is a good idea to try to break applications into smaller independent parts which can be developed in a separate project, built separately, and deployed separately from each other.

Ideas Behind Micro Frontends

Each team should be able to choose and upgrade their stack without having to coordinate with other teams. A service can be built on Vue, and another one on React also another service can be built on plain js.

Micro frontend sample architecture deployment flow

Being technology agnostic

The technology agnostic approach means that any specific technology can be used to solve your business problems.

You have to ensure that your team is able to look beyond simply applying a particular technology.

Isolating team code base

Don’t share a runtime, even if all teams use the same framework. Build independent apps that are self-contained.

Create team prefixes

Like naming conventions, namespacing on CSS to avoid collisions and clarify ownership.

Integration Approaches

Conceptually, a micro frontend architecture is just a bunch of smaller frontend applications snapped together. There are lots of ways to achieve this. However, most implementations fall into one of three buckets.

Build-time composition

Composing components at build-time is pretty much just putting a different approach on traditional frontend dependency management. For example, adding your self-contained micro frontends as application dependency.

One approach is publishing each micro frontend as a package, then creating the container application that includes them all as library dependencies.

Build-time composition

This approach means that we have to re-compile and release every single micro frontend in order to release a change to any individual part of the product.

In the built-time composition approach, separately deployed packages are the dependencies of the main application. Let’s add the package dependencies into the package.json file:

// package.json
{

“dependencies”: {
“@organisation/profile-page”: “¹.1.2”,
“@organisation/search-page”: “¹.4.5”,
“@organisation/cart-page”: “³.1.1”
},

}

Import the packages in the route file and define the routes:

// routes.js

import DetailPage from '@organisation/detail-page';
import SearchPage from '@organisation/search-page';
import CartPage from '@organisation/ucartser-page';

export default routes = [
{
path: '/detail',
name: 'DetailPage',
component: DetailPage
},
{
path: '/search',
name: 'SearchPage',
component: SearchPage
},
{
path: '/cart',
name: 'CartPage',
component: CartPage
}
]

Client-side composition

Client-side composition allows us to fetch resources from independently deployed URLs. Also, It makes it easier to isolate releases and deployments. This is one of the patterns that combine fragments on the client-side.

Client-side composition

The implementation of this approach is similar to runtime composition, but components are deployed separately and then used in the main application.

Import components to the route file as we did in the built-time composition approach:

// routes.js

import DetailPage from 'src/components/detail-page.vue';
import SearchPage from 'src/components/search-page.vue';
import CartPage from 'src/components/ucartser-page.vue';

export default routes = [
{
path: '/detail',
name: 'DetailPage',
component: DetailPage
},
{
path: '/search',
name: 'SearchPage',
component: SearchPage
},
{
path: '/cart',
name: 'CartPage',
component: CartPage
}
]

Create the component file and inject the micro frontend js bundle after mounting the component:

// src/components/detail-page.vue<template>
<div id="fragment-container"></div>
</template>

<script lang="ts">
import { defineComponent, onMounted } from 'vue';
export default defineComponent({
name: 'FragmentComponent',
setup () {
onMounted(() => {
const script = document.createElement('script')
script.src = "www.url-to-app-repo.com/detail-page.js"
script.onload = () => window.renderPage('fragment-container')

document.head.appendChild(script)
})
}
})
</script>

Last step is creating a micro frontend app. In this example, the main application container is written in Vue but, micro app written is React.

// www.url-to-app-repo.com/detail-page.js

import React from 'react';
import ReactDOM from 'react-dom';
import DetailPage from './DetailPage';

window.renderPage = (id) => {
ReactDOM.render(
<DetailPage />,
document.getElementById(id),
);
};

Server-side composition

This approach involves composing micro frontends on the server-side and rendering HTML on the server out of multiple templates or fragments. We have an index.html document which contains any common page elements, and then uses server-side. Later then, it includes a plugin page-specific content from fragment HTML files.

Server-side composition

Implementations for server-side composition are like old-school server-side templating.

Index.html file:

<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Main App</title>
</head>
<body>
<h1>Title</h1>
<!--# include file="$PAGE.html" -->
</body>
</html>

We serve this file using Nginx, configuring the $PAGE variable by matching against the URL that is being requested:

server {
listen 8080;
server_name localhost;

root /usr/share/nginx/html;
index index.html;
ssi on;

# Redirect / to /browse
rewrite ^/$ http://localhost:8080/browse redirect;

# Decide which HTML fragment to insert based on the URL
location /cart {
set $PAGE cart;
}
location /profile {
set $PAGE 'profile'
}

# All locations should render through index.html
error_page 404 /index.html;
}

This is fairly standard server-side composition. Independent teams have their own deployment pipeline that allows them to deploy changes to one page without affecting any other page.

Conclusion

As frontend codes become larger and more complex over the years, it can be seen that there is a need for more scalable architectures.

Dividing a large frontend application into smaller chunks has the potential to reduce technical conflicts in an organization, but the necessity should be discussed before doing so. If you have a regular company website and a small project with a small team working on it, then most likely you don’t need a micro frontend concept, but if you have several teams on your project and several interacting views on different domains, then it makes sense to consider this approach.

What do you think about the micro frontend? Will it be the future of front end development? Let me know your thoughts in the comment section below.

Thanks for reading!

Resources

--

--

Ahmet Önol
Insider Engineering

Senior Frontend Developer at Insider | Senior Full-Stack Developer