DEV Community

Benjy
Benjy

Posted on

Mirco Front-end Fundamentals

Hello, I am Benjy, a newbie in front end. Below will be my take on mircro front end architecture.


What is Micro Front-end?

Microfrontend is an architecture inspired by microservice. What is microservice? Long words short, microservice is a backend architecture that has an API gateway redirect request to respective services.

As what I said just now, microfrontend is conceptually inspired from microservice, hence they somehow look similar.


Micro frontends are an architectural approach that breaks large, monolithic web applications into smaller, independent, and specialized frontend modules.

A master/shell app will act as a container to contain all the microfrontends. Now, the master app will act as a "gateway" to redirect user or to decide which microfrontend to be rendered.


Why Microfrontend?

Imagine today you are setting up a web app for your company to manage internal resources. You started with payroll module, then your web app will look something like this:

Then, your boss wanted to include a leave system. So, you add in the leave module, and a sidebar for user to navigate between modules:

Moving on, your boss wanted to add in more modules, then your web app will look something like:

As your system grows, the codebase become larger and larger. Each iteration will consume more development, testing and deployment effort. Also, the growing codebase will become difficult to maintain.

Hence, microfrontend comes into the rescue. You split related modules into different sub-modules, each maintained in different microfrontend repo. Then, you have a master app combining each of the microfrontends (submodules).

Now, the system become granular and easy to maintain and test. You reduce maintenence effort and earn yourself a promotion (yay!).

Key Characteristics & Benefits of microfrontend:

  • Independent Deployment: Teams can release updates to their specific feature (e.g., checkout) without redeploying the entire application.
  • Technology Agnostic: Teams can choose or upgrade their own stack (React, Angular, Vue) without coordinating with others, allowing for gradual updates.
  • Autonomous Teams: Organized around business domains rather than technical layers, enabling faster development cycles.
  • Improved Scalability: Large, complex frontend codebases become easier to manage by breaking them down into manageable pieces.

But,

Microfrontend has its drawbacks too. For example, how to manage shared states between sub-modules? How to ensure UI unity? How can sub-modules communicate with each other? And many more ......

These are the things you need to consider when deciding between monolithic and microfrontends architecture. When your project is small, monolithic is good enough. Evaluate the effort before making decision.


How Microfrontends Work (Basic Idea)

Each microfrontend is built and deployed independently, usually as static assets (JavaScript, CSS) hosted on a server.

The main application dynamically loads these assets at runtime and integrates them into the page by calling a predefined interface (e.g., a mount function).


A Simple Implementation (Concept Demonstration)

This is a simplified implementation for learning purposes and does not handle real-world concerns such as dependency sharing, style isolation, or performance.

Sub App

Lets start with creating a new react project using Vite

In terminal, run:

npm create vite@latest react-app -- --template react
Enter fullscreen mode Exit fullscreen mode

You will see:

The page is too complicated to me, I simplify the page into:

main.jsx

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <App />
  </StrictMode>,
)
Enter fullscreen mode Exit fullscreen mode

App.jsx

import { useState } from 'react'
import './App.css'

function App() {
  const [count, setCount] = useState(0)

  return (
    <div className="sub-app-container-1">
      <button
        className="count-btn"
        onClick={() => setCount((prev) => prev + 1)}
      >
        Count: {count}
      </button>

      <button
        className="reset-btn"
        onClick={() => setCount(0)}
      >
        reset
      </button>
    </div>
  )
}

export default App

Enter fullscreen mode Exit fullscreen mode

App.css

.sub-app-container-1 {
  background: linear-gradient(90deg,rgba(42, 123, 155, 1) 0%, rgba(87, 192, 199, 1) 50%, rgba(83, 232, 237, 1) 100%);
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  justify-content: center;
  align-items: center;
  height: 100%;
}

.count-btn {
  background: yellow;
  padding: 6px 12px;
  border-radius: 4px;
  border: 2px solid gray;
  font-size: larger;
}

.count-btn:hover {
  cursor: pointer;
}

.reset-btn:hover {
  cursor: pointer;
}
Enter fullscreen mode Exit fullscreen mode

Now my react app looks like this (a very simple website with counting function)

Let's proceed to the server.

Server

Let's create an express.js server to store our built file from sub app.

In terminal:

mkdir server        
cd server        
npm init -y
npm install express 
Enter fullscreen mode Exit fullscreen mode

Then initiate your repo like this:

app.js

const express = require('express');
const cors = require('cors');
const path = require('path');

const app = express();
const PORT = process.env.PORT || 3000;

app.use(cors());

app.use(express.static(path.join(__dirname, 'static')));

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(PORT, () => {
  console.log(`Server is running at http://localhost:${PORT}`);
  console.log(`Static path: ${path.join(__dirname, 'static')}`)
});

Enter fullscreen mode Exit fullscreen mode

Then, start your server in terminal:

node app.js
Enter fullscreen mode Exit fullscreen mode

And you will see something like this:

Go back to your react repo and build it

npm run build
Enter fullscreen mode Exit fullscreen mode



You will see the built file under /dist/assets

Copy the .js and .css files and paste them into server/static



To test whether are these files accessible, you can try to access them in browser:

http://localhost:3000/index-CX8UIe2c.js
http://localhost:3000/index-CX8UIe2c.css
Enter fullscreen mode Exit fullscreen mode

You will see something like this:



If you are able to access both file, we can proceed to the main app.

Main App

Let's create main/index.html

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Micro FE Demo</title>
  <style>
    #root {
      width: 80%;
      border: 2px solid red;
      height: 600px;
    }

    h1 {
      font-size: larger;
      font-weight: bold;
    }
  </style>
</head>
<body>
  <h1>I am main app, below is sub app</h1>

  <div id="root">

  </div>

  <script>
  </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Note:

We have to name our sub-app container's id to #root, because React app will mount on #root by default. Normally, this is not the standard practice to hard-code the container's id name. But since this is a demo, we will not go deep into this.

Run the index.html (main app):

And you will see something like this:

Then, here's the magic happens. We will try to make request to the server to get the build files, and mount into our main app.

We will implement the process above in <script>.

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Micro FE Demo</title>
  <style>
    #root {
      width: 80%;
      border: 2px solid red;
      height: 600px;
    }

    h1 {
      font-size: larger;
      font-weight: bold;
    }
  </style>
</head>
<body>
  <h1>I am main app, below is sub app</h1>

  <div id="root">

  </div>

  <script>
    fetch("http://localhost:3000/index-CX8UIe2c.js")
      .then(res => {
        return res.text()
      })
      .then((text) => {
        eval(text)
      })

    fetch("http://localhost:3000/index-CKZWWwUF.css")
      .then(res => {
        return res.text()
      })
      .then(text => {
        const dom = document.createElement("style");
        dom.textContent = text;
        document.body.appendChild(dom)
      })
  </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

AND.......... tada!

You have successfully implemented microfrontend from scratch.

Conclusion

In this note, we explored micro frontend architecture from first principles — what it is, why it exists, and how it works under the hood. By breaking a large monolithic frontend into independently deployable modules, teams gain flexibility, autonomy, and a more manageable codebase as the system scales.
The demo above is deliberately simplified. In practice, you would want to address concerns like:

  • Style isolation — using Shadow DOM or CSS scoping to prevent style leakage between sub-apps
  • Shared dependencies — avoiding duplicate bundles (e.g., two copies of React) using tools like Webpack Module Federation
  • State synchronization — using a shared event bus or a state manager accessible to all sub-apps
  • Routing — coordinating navigation between the shell app and sub-apps

Tools like Webpack Module Federation, Single-SPA, and Qiankun solve these problems in production-grade setups and are worth exploring next.
As with any architectural decision, microfrontends are not a silver bullet. For small projects, a well-structured monolith is simpler and easier to reason about. Reach for microfrontends when team autonomy, independent deployability, or codebase scale genuinely demands it.



Thanks for your time. Cheers! :D

Top comments (0)