Codementor Events

Introduction to Micro-Frontends Architecture in React JS

Published Jun 30, 2024Last updated Jul 02, 2024
Introduction to Micro-Frontends Architecture in React JS

In today's article, I will thoroughly explain the Micro-Frontend architecture in detail, using an example in React JS. This will help you write maintainable and scalable code for your projects. Choosing the appropriate coding architecture is a best practice for any project, regardless of its size.

Micro-frontends is an architectural style where the traditionally monolithic frontend codebase is split into smaller, more manageable pieces. These pieces, or "micro-frontends," can be developed, tested, and deployed independently, enabling teams to work more efficiently and scale their projects more effectively. This approach mirrors the microservices architecture in the backend, aiming to achieve the same benefits of modularity and scalability.

What Are Micro-Frontends?

Micro-frontends are essentially the frontend equivalent of microservices. In a microservices architecture, a large application is divided into smaller, self-contained services that can be developed and deployed independently. Similarly, in a micro-frontends architecture, a large frontend application is divided into smaller, self-contained frontend components or "micro-apps" that can be developed and deployed independently.

Benefits of Micro-Frontends

1. Independent Development: Teams can work on different parts of the application simultaneously without interfering with each other.

2. Scalability: Different parts of the frontend can be scaled independently based on need.
3. Technology Agnostic: Different micro-frontends can be built using different technologies or frameworks, allowing teams to choose the best tools for their specific needs.
4. Improved Maintainability: Smaller, well-defined codebases are easier to maintain and understand.
5. Faster Releases: Independent deployment means that changes in one part of the application can be released without requiring a full deployment of the entire frontend.

Challenges of Micro-Frontends

1. Increased Complexity: Coordinating multiple micro-frontends can be complex, especially when ensuring consistent styling and shared state.
2. Performance Overheads: Loading multiple micro-frontends can introduce performance overheads, such as increased initial load times.
3. Integration: Ensuring seamless integration and communication between micro-frontends can be challenging.

Implementing Micro-Frontends

Implementing micro-frontends involves several key steps, including defining the boundaries of each micro-frontend, choosing a suitable integration method, and ensuring seamless communication between micro-frontends. Let's explore these steps in more detail.

1. Defining Boundaries
The first step is to identify the boundaries of each micro-frontend. These boundaries can be based on different criteria, such as:

- Feature-Based: Each micro-frontend represents a specific feature or set of features (e.g., user profile, dashboard, settings).
- Domain-Based: Each micro-frontend corresponds to a specific business domain or subdomain (e.g., payments, notifications, user management).
- Page-Based: Each micro-frontend represents a separate page or set of pages within the application (e.g., home page, product page, checkout page).

2. Integration Methods
There are several methods for integrating micro-frontends, each with its own advantages and trade-offs:

2.1. Server-Side Integration
In server-side integration, the server assembles the different micro-frontends and serves them as a single HTML page. This method ensures that the initial load is fast and that SEO is not compromised. However, it can introduce additional complexity on the server side.

2.2. Client-Side Integration
In client-side integration, the micro-frontends are loaded and assembled on the client side using JavaScript. This method offers greater flexibility and allows for more dynamic loading of micro-frontends. However, it can result in slower initial load times.

2.3. Edge-Side Includes (ESI)
Edge-Side Includes (ESI) is a web standard that allows the assembly of micro-frontends at the edge (i.e., CDN level). This method offers a balance between server-side and client-side integration, providing fast load times and dynamic content assembly.

3. Communication Between Micro-Frontends
Ensuring seamless communication between micro-frontends is crucial for a cohesive user experience. There are several methods for achieving this:

3.1. Custom Events
Micro-frontends can communicate using custom events. This method involves dispatching and listening for custom events on the DOM.

Example:

// Dispatching an event from one micro-frontend
const event = new CustomEvent('userLoggedIn', { detail: { userId: 123 } });
window.dispatchEvent(event);

// Listening for the event in another micro-frontend
window.addEventListener('userLoggedIn', (event) => {
  console.log('User logged in:', event.detail.userId);
});

3.2. Shared State
A shared state management solution, such as Redux or Zustand, can be used to maintain a consistent state across micro-frontends.

Example with Zustand:

// Creating a shared state store
import create from 'zustand';

const useStore = create((set) => ({
  user: null,
  setUser: (user) => set({ user }),
}));

// Using the store in a micro-frontend
import { useStore } from './store';

const LoginComponent = () => {
  const setUser = useStore((state) => state.setUser);

  const login = (user) => {
    // Perform login logic
    setUser(user);
  };

  return (
    <button onClick={() => login({ id: 123, name: 'John Doe' })}>Login</button>
  );
};

3.3. Shared Services
Shared services, such as a centralized API service, can facilitate communication between micro-frontends.

Example:

// Shared API service
const apiService = {
  getUser: () => fetch('/api/user').then((response) => response.json()),
};
// Using the service in a micro-frontend
apiService.getUser().then((user) => {
  console.log('User data:', user);
});

Example: Building a Micro-Frontends Application

Let's build a simple example of a micro-frontends application using React. We'll create two micro-frontends: UserProfile and Dashboard.

Step 1: Setting Up Micro-Frontends
1.1. UserProfile Micro-Frontend
Create a new React project for the UserProfile micro-frontend:

npx create-react-app user-profile
cd user-profile

Modify the App.js file to display user profile information:

// user-profile/src/App.js
import React from 'react';

const App = () => {
  return (
    <div>
      <h1>User Profile</h1>
      <p>Name: John Doe</p>
      <p>Email: john.doe@example.com</p>
    </div>
  );
};

export default App;

1.2. Dashboard Micro-Frontend
Create a new React project for the Dashboard micro-frontend:

npx create-react-app dashboard
cd dashboard

Step 2: Integrating Micro-Frontends
We'll use the import-html-entry library to dynamically load and integrate the micro-frontends. First, install the library:

npm install import-html-entry

Modify the index.html file of the main application to include placeholders for the micro-frontends:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Main Application</title>
</head>
<body>
  <div id="root"></div>
  <div id="micro-frontend-user-profile"></div>
  <div id="micro-frontend-dashboard"></div>
</body>
</html>

Modify the App.js file of the main application to load the micro-frontends:

// main-app/src/App.js
import React, { useEffect } from 'react';
import { importEntry } from 'import-html-entry';

const loadMicroFrontend = async (url, containerId) => {
  const { execScripts } = await importEntry(url);
  execScripts(window, { container: `#${containerId}` });
};

const App = () => {
  useEffect(() => {
    loadMicroFrontend('http://localhost:3001', 'micro-frontend-user-profile');
    loadMicroFrontend('http://localhost:3002', 'micro-frontend-dashboard');
  }, []);

  return (
    <div>
      <h1>Main Application</h1>
    </div>
  );
};

export default App;

Ensure that the UserProfile and Dashboard micro-frontends are running on different ports:

# In user-profile project
npm start --port 3001

# In dashboard project
npm start --port 3002

Step 3: Running the Main Application
Start the main application:

npm start

The main application should now display the UserProfile and Dashboard micro-frontends:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Main Application</title>
</head>
<body>
  <div id="root"></div>
  <div id="micro-frontend-user-profile">
    <div>
      <h1>User Profile</h1>
      <p>Name: John Doe</p>
      <p>Email: john.doe@example.com</p>
    </div>
  </div>
  <div id="micro-frontend-dashboard">
    <div>
      <h1>Dashboard</h1>
      <p>Welcome to your dashboard, John Doe!</p>
    </div>
  </div>
</body>
</html>

Conclusion

Micro-frontends architecture offers a modular approach to frontend development, enabling teams to work independently, improve scalability, and maintainability. While it introduces some complexity, the benefits often outweigh the challenges, especially for large-scale applications. By defining clear boundaries, choosing suitable integration methods, and ensuring seamless communication between micro-frontends, you can effectively implement micro-frontends architecture in your projects.

Follow Me for more interested Article

Join me on Codementor for more helpful tips. Make sure to like and Follow to stay in the loop with my latest articles on different topics including programming tips & tricks, tools, Framework, Latest Technologies updates.

View my Blogs
Please support me on PATREON on this link.

Support me on Patreon

I would love to see you in the followers list.

Discover and read more posts from Rizwan
get started
post comments1Reply
chanolandam
2 days ago

Micro-frontends architecture offers significant benefits in terms of scalability, maintainability, and flexibility. By decomposing the frontend into smaller, independent pieces, teams can develop https://honistas.com/honista-v7-1-apk/ and deploy features more efficiently. React JS, with its component-based architecture, is particularly well-suited for micro-frontends, and tools like Webpack 5’s Module Federation and Single-SPA make implementation more manageable.