Table of Contents
What is micro-front end
A micro frontend is an architectural pattern in web development where a web application's user interface (UI) is divided into smaller, independently developed and deployable modules.This approach extends the principles of microservices to the frontend, allowing multiple teams to work on different parts of the UI concurrently, each using its own technology stack if desired.
Key Characteristics:
- Independence: Each micro frontend is a standalone application with its own repository, build process, and deployment pipeline.
- Team Autonomy: Teams can develop, test, and deploy their micro frontends independently, facilitating parallel development and reducing dependencies.
- Technology Agnosticism: Different micro frontends can be built using different frameworks or libraries, such as React, Angular, or Vue.js, based on the team's expertise and project requirements.
Benefits:
- Scalability: Breaking down a monolithic frontend into micro frontends allows for better scalability and maintainability, as each module can be scaled independently.
- Incremental Upgrades: Teams can update or rewrite parts of the frontend without affecting the entire application, enabling gradual modernization.
- Improved Collaboration: Multiple teams can work simultaneously on different features, accelerating development and reducing time-to-market.
Challenges:
- Integration Complexity: Combining multiple micro frontends into a seamless user experience requires careful orchestration and consistent design practices.
- Performance Overhead: Loading multiple micro frontends may introduce performance challenges, such as increased load times due to duplicated dependencies.
- Shared State Management: Managing shared state or global variables across micro frontends can be complex and may require additional coordination.
References:
- https://martinfowler.com/articles/micro-frontends.html
- https://micro-frontends.org
- https://stackoverflow.com/questions/60548251/how-do-you-share-state-in-a-micro-frontend-scenario
- https://en.wikipedia.org/wiki/Micro_frontend
- https://single-spa.js.org/docs/microfrontends-concept
Tools that provide micro-frontend arch
1. Webpack Module Federation
- Description: A feature in Webpack 5 that allows applications to share and load modules dynamically at runtime, enabling seamless code sharing and micro frontend integration.
- Key Features:
- Dynamic module loading across applications.
- Shared dependencies (e.g., React, utilities).
- Independent deployment and runtime integration.
- Documentation: Webpack Module Federation
2. Single-SPA
- Description: A popular framework for building micro frontends, enabling you to use multiple frameworks (e.g., React, Angular, Vue) in a single application.
- Key Features:
- Orchestrates multiple frameworks.
- Manages lifecycle events of micro frontends.
- Provides routing between different micro frontends.
- Documentation: Single-SPA Official Website
3. Qiankun
- Description: A micro frontend framework based on Single-SPA but optimized for seamless integration.
- Key Features:
- Easy integration with existing applications.
- Built-in sandboxing for better isolation of micro frontends.
- Supports independent deployment of sub-applications.
- Documentation: Qiankun Official Website
4. Web Components
- Description: A native browser technology that allows you to create reusable components encapsulating functionality, style, and structure.
- Key Features:
- Framework-agnostic.
- Native browser support (Custom Elements, Shadow DOM).
- Good for building highly isolated micro frontends.
- Documentation: MDN Web Components Guide
5. Tailor (for Server-Side Rendering)
- Description: A layout service for assembling micro frontends on the server side.
- Key Features:
- Assembles fragments on the server and streams them to the client.
- Optimized for performance with Edge Side Includes (ESI).
- Documentation: Tailor on GitHub
6. Luigi Framework
- Description: A micro frontend framework specifically designed for enterprise-grade applications.
- Key Features:
- Centralized navigation and routing management.
- Supports secure communication between micro frontends.
- Lightweight and highly extensible.
- Documentation: Luigi Framework Official Website
7. Piral
- Description: A framework for building modular applications with micro frontends.
- Key Features:
- Enables the creation of a shell application.
- Offers shared services for state, communication, and APIs.
- Provides a marketplace for micro frontend modules.
- Documentation: Piral Official Website
8. FrintJS
- Description: A modular JavaScript framework for creating micro frontends.
- Key Features:
- Lightweight and flexible.
- Supports hierarchical dependency injection.
- Works well for React-based micro frontends.
- Documentation: FrintJS Official Website
9. iFrame-based Solutions
- Description: Using iframes for micro frontends allows strict isolation but has limitations in terms of shared state and seamless user experience.
- Key Features:
- Total isolation of styles and scripts.
- Simple integration.
- Best for integrating legacy applications.
Comparison Table
| Tool | Best Use Case | Key Advantage |
|---|---|---|
| Module Federation | Shared code and runtime-loaded micro frontends | Efficient, framework-agnostic, and enables independent deployments |
| Single-SPA | Multiple frameworks in a single app | Mature and well-documented |
| Qiankun | Enterprise-grade micro frontends | Sandbox isolation |
| Web Components | Framework-agnostic micro frontends | Native support, encapsulation |
| Tailor | Server-side composition | Performance with ESI |
| Luigi Framework | Secure, large-scale applications | Centralized routing and extensibility |
| Piral | Modular frontend applications | Integrated state and service sharing |
| FrintJS | React-based micro frontends | Dependency injection for components |
| iFrames | Legacy app integration, strict isolation | Simple but less flexible |
The best tool for implementing micro frontends depends on your specific project requirements, team structure, and technical constraints. Here's a breakdown to help you choose:
1. Webpack Module Federation
- Best For:
- Sharing dependencies at runtime with efficient bundling.
- Teams working with modern frameworks (React, Vue, Angular, etc.).
- Projects needing independent deployments with minimal orchestration.
- Advantages:
- Highly efficient with shared dependencies.
- Minimal additional setup if you're already using Webpack.
- Great for incremental migration of legacy apps.
- Drawbacks:
- Requires understanding Webpack configuration.
- Limited lifecycle management compared to frameworks like Single-SPA.
- When to Choose: If you want dynamic module loading and runtime sharing with minimal orchestration.
2. Single-SPA
- Best For:
- Projects using multiple frameworks (e.g., React, Angular, Vue) in the same application.
- Teams looking for robust lifecycle management for micro frontends.
- Advantages:
- Flexible and well-documented.
- Excellent for orchestrating micro frontends with routing.
- Drawbacks:
- Requires more configuration and boilerplate code compared to Module Federation.
- Performance can be a concern if not optimized properly.
- When to Choose: If you need a framework-agnostic solution or are combining multiple technologies.
3. Qiankun
- Best For:
- Enterprise-grade micro frontend projects.
- Teams needing sandboxing for better isolation between micro frontends.
- Advantages:
- Built-in sandbox for style/script isolation.
- Simple integration with existing apps.
- Drawbacks:
- Opinionated implementation, which may limit flexibility.
- When to Choose: If you need isolation and modularization with straightforward integration.
4. Web Components
- Best For:
- Framework-agnostic and reusable micro frontends.
- Highly isolated components with minimal dependency on external tools.
- Advantages:
- Native browser support (no extra tools required).
- Fully encapsulated styles and logic.
- Drawbacks:
- Requires polyfills for older browsers.
- Limited in scope compared to frameworks like Single-SPA or Qiankun.
- When to Choose: If you want lightweight and native micro frontend components.
5. Tailor (Server-Side Composition)
- Best For:
- Applications requiring server-side rendering (SSR).
- Assembling micro frontends on the server before streaming to the client.
- Advantages:
- Performance benefits with server-side composition.
- Compatible with Edge Side Includes (ESI) for caching.
- Drawbacks:
- Less suitable for client-side heavy apps.
- When to Choose: If you prioritize server-side rendering or work with SSR frameworks.
6. Luigi Framework
- Best For:
- Enterprise applications requiring secure and centralized routing.
- Advantages:
- Strong support for security and extensibility.
- Focused on large-scale, secure micro frontends.
- Drawbacks:
- Less flexible for smaller projects.
- When to Choose: If you need a secure and scalable micro frontend framework.
7. Piral
- Best For:
- Teams looking for a plug-and-play solution to build modular apps with shared services.
- Advantages:
- Provides a full ecosystem for micro frontends.
- Built-in marketplace for modules.
- Drawbacks:
- Opinionated and less flexible than standalone tools.
- When to Choose: If you want a pre-built modular architecture.
8. iFrame-Based Solutions
- Best For:
- Integrating legacy systems or achieving strict isolation between micro frontends.
- Advantages:
- Total isolation of styles and scripts.
- Simple implementation.
- Drawbacks:
- Poor UX (e.g., cross-domain communication issues).
- Limited shared state and performance overhead.
- When to Choose: If integrating legacy apps is your priority.
Which Tool Should You Choose?
| Use Case | Recommended Tool |
|---|---|
| Efficient runtime code sharing | Webpack Module Federation |
| Combining multiple frameworks | Single-SPA |
| Enterprise-grade modularization | Qiankun |
| Lightweight and framework-agnostic | Web Components |
| Server-side rendering (SSR) | Tailor |
| Security-focused enterprise apps | Luigi Framework |
| Pre-built modular architecture | Piral |
| Integrating legacy systems | iFrames |
Companies That Using Micro-Frontend
1. Module Federation
- Shopify
- Adidas
- Etc: https://module-federation.io/showcase/
2. iFrame
- Google: https://www.linkedin.com/pulse/micro-frontend-architecture-iframes-x-module-vinicius-marson-k1hwf
- Spotify: https://lucamezzalira.com/category/micro-frontends/?utm_source=chatgpt.com
- I believe many companies use iFrame but I couldn’t find the article
3. Web Components
- Google, Amazon, Salesforce, and Zalando: https://medium.com/swlh/micro-frontend-using-web-components-e9faacfc101b
- GitHub: https://github.blog/engineering/architecture-optimization/how-we-use-web-components-at-github/
- Youtube, I believe it was using web component but I coulnd’t find article for that
4. Piral
Micro Front End using Module Federation
To create a micro front-end architecture where each remote app is hosted independently and integrated into the host application, we can extend the previous example. Here, each remote app will have its own deployment and can be served from different domains or ports.
Objective
- Host app will dynamically fetch and render micro front-ends hosted on separate servers:
- Remote A served at
http://localhost:4001/remoteEntry.js - Remote B served at
http://localhost:4002/remoteEntry.js
- Remote A served at
Directory Structure
micro-frontend/
├── host/
│ ├── src/
│ │ ├── App.js
│ │ ├── bootstrap.js
│ │ └── index.js
│ ├── webpack.config.js
│ └── package.json
├── remote-a/
│ ├── src/
│ │ ├── App.js
│ │ ├── bootstrap.js
│ │ └── index.js
│ ├── webpack.config.js
│ └── package.json
├── remote-b/
│ ├── src/
│ │ ├── App.js
│ │ ├── bootstrap.js
│ │ └── index.js
│ ├── webpack.config.js
│ └── package.json
Step 1: Host Configuration
host/webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
entry: './src/index',
mode: 'development',
devServer: {
port: 4000,
},
output: {
publicPath: 'http://localhost:4000/',
},
plugins: [
new ModuleFederationPlugin({
name: 'host',
remotes: {
remoteA: 'remoteA@http://localhost:4001/remoteEntry.js',
remoteB: 'remoteB@http://localhost:4002/remoteEntry.js',
},
shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
}),
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-react'],
},
},
},
],
},
resolve: {
extensions: ['.js', '.jsx'],
},
};
Step 2: Remote A Configuration
remote-a/webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
entry: './src/index',
mode: 'development',
devServer: {
port: 4001,
},
output: {
publicPath: 'http://localhost:4001/',
},
plugins: [
new ModuleFederationPlugin({
name: 'remoteA',
filename: 'remoteEntry.js',
exposes: {
'./RemoteAppA': './src/App',
},
shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
}),
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-react'],
},
},
},
],
},
resolve: {
extensions: ['.js', '.jsx'],
},
};
Step 3: Remote B Configuration
remote-b/webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
entry: './src/index',
mode: 'development',
devServer: {
port: 4002,
},
output: {
publicPath: 'http://localhost:4002/',
},
plugins: [
new ModuleFederationPlugin({
name: 'remoteB',
filename: 'remoteEntry.js',
exposes: {
'./RemoteAppB': './src/App',
},
shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
}),
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-react'],
},
},
},
],
},
resolve: {
extensions: ['.js', '.jsx'],
},
};
Step 4: Components and Entry Points
remote-a/src/App.js
import React from 'react';
const RemoteAppA = () => <div>Remote App A Content</div>;
export default RemoteAppA;
remote-b/src/App.js
import React from 'react';
const RemoteAppB = () => <div>Remote App B Content</div>;
export default RemoteAppB;
host/src/App.js
import React, { Suspense } from 'react';
const RemoteAppA = React.lazy(() => import('remoteA/RemoteAppA'));
const RemoteAppB = React.lazy(() => import('remoteB/RemoteAppB'));
const App = () => (
<div>
<h1>Host Application</h1>
<Suspense fallback={<div>Loading Remote A...</div>}>
<RemoteAppA />
</Suspense>
<Suspense fallback={<div>Loading Remote B...</div>}>
<RemoteAppB />
</Suspense>
</div>
);
export default App;
Bootstrap and Entry Files
For all three apps, add the following files:
bootstrap.js:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
index.js:
import('./bootstrap');
Step 5: HTML Templates
Minimal index.html for each app:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Micro Frontend</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
Step 6: Start Applications
- Start Remote A:
cd remote-a
npx webpack serve
- Start Remote B:
cd remote-b
npx webpack serve
- Start Host:
cd host
npx webpack serve
Results
- Remote A hosted at
http://localhost:4001. - Remote B hosted at
http://localhost:4002. - Host application fetching and rendering micro front-ends dynamically at
http://localhost:4000.
Visit http://localhost:4000 to see the host application displaying content from independently hosted remote apps.
Video how to use module federation:
https://www.youtube.com/watch?v=OmLsV8Dljzo
Creating a micro-frontend architecture using web components allows you to integrate applications built with different technologies (e.g., React, Angular, Vue, etc.) into a single cohesive application. Here's how you can achieve it step by step:
How to use Web Components
1. Understand Web Components as Micro-Frontend Shells
Web components provide:
- Custom Elements: Define your components with standard APIs.
- Shadow DOM: Encapsulate styles and markup, ensuring isolation.
- HTML Templates: Reusable templates for UI elements.
Each micro-frontend can be developed as a web component and integrated into a host application.
2. Plan the Architecture
- Host Application: A container application that loads and manages web components.
- Micro-Frontend Applications: Independent modules implemented as web components, each using its own technology stack.
3. Steps to Build Micro-Frontends
Step 1: Create a Web Component in Each Technology
React
- Install dependencies:
npm install react react-dom
- Create a web component wrapper:
import React from 'react';
import ReactDOM from 'react-dom';
import MyReactApp from './MyReactApp';
class ReactWebComponent extends HTMLElement {
connectedCallback() {
const mountPoint = document.createElement('div');
this.attachShadow({ mode: 'open' }).appendChild(mountPoint);
ReactDOM.render(<MyReactApp />, mountPoint);
}
disconnectedCallback() {
ReactDOM.unmountComponentAtNode(this.shadowRoot.firstChild);
}
}
customElements.define('react-web-component', ReactWebComponent);
Vue
- Install dependencies:
npm install vue
- Create a Vue web component:
import { defineCustomElement } from 'vue';
import MyVueApp from './MyVueApp.vue';
const VueWebComponent = defineCustomElement(MyVueApp);
customElements.define('vue-web-component', VueWebComponent);
Angular
- Use Angular's
@angular/elementspackage:
ng add @angular/elements
- Create a custom element:
import { createCustomElement } from '@angular/elements';
import { Injector } from '@angular/core';
import { MyAngularComponent } from './my-angular.component';
const MyAngularElement = createCustomElement(MyAngularComponent, { injector });
customElements.define('angular-web-component', MyAngularElement);
Step 2: Deploy Micro-Frontends
Deploy each micro-frontend to its own domain, subdomain, or path.
Examples:
- React:
https://example.com/react - Vue:
https://example.com/vue - Angular:
https://example.com/angular
Step 3: Integrate into the Host Application
The host application can dynamically load web components from these deployments.
- Load Web Components: Add a script tag for each component:
<script src="https://example.com/react/main.js"></script>
<script src="https://example.com/vue/main.js"></script>
<script src="https://example.com/angular/main.js"></script>
- Use the Components: Include the custom elements in the host app:
<react-web-component></react-web-component>
<vue-web-component></vue-web-component>
<angular-web-component></angular-web-component>
4. Handle Communication Between Components
Use a shared event bus or custom events for communication:
// Dispatching an event
document.dispatchEvent(new CustomEvent('some-event', { detail: { data: 'example' } }));
// Listening to an event
document.addEventListener('some-event', (e) => {
console.log(e.detail.data);
});
5. Benefits of Using Web Components
- Framework Agnostic: Allows combining different frameworks in the same project.
- Encapsulation: Web components ensure style and behavior isolation.
- Reusability: Components can be reused across multiple projects.
By following this approach, you can create a flexible, scalable micro-frontend architecture where each team can use their preferred technology stack while maintaining seamless integration.
Conclusion
Web components are lightweight because they use native browser APIs and avoid framework-specific runtime overhead. They are framework-agnostic because they are based on standardized web technologies, making them compatible with any framework or standalone application. These characteristics make web components ideal for building reusable and independent UI elements.
Using iFrames as micro-frontend
Using iFrames to implement a micro-frontend architecture is one of the oldest and simplest approaches. iFrames provide strong isolation between different applications, making them suitable for use cases where strict separation of code and styles is required. Here's how you can implement a micro-frontend architecture using iFrames:
1. When to Use iFrames for Micro-Frontends
Advantages:
- Strong Isolation: Each micro-frontend runs in its own window context, ensuring no conflicts in styles, scripts, or runtime.
- Framework Agnostic: You can use any framework or technology in each micro-frontend.
- Independent Deployment: Micro-frontends can be hosted on different domains, subdomains, or servers.
Disadvantages:
- Performance Overhead: iFrames are slower to load and interact with compared to other techniques like Module Federation or web components.
- Complex Communication: Interacting between the host and the iFrames can be cumbersome and less secure.
2. Architecture Overview
- Host Application: The main application containing the iFrames.
- Micro-Frontends: Separate applications loaded within the iFrames.
3. Steps to Create Micro-Frontends Using iFrames
Step 1: Build Individual Micro-Frontend Applications
Develop each micro-frontend as a standalone application. These applications should be deployed independently.
For example:
https://cart.example.com(Cart app)https://profile.example.com(Profile app)https://search.example.com(Search app)
Step 2: Host Application with iFrames
The host application is responsible for loading and displaying the iFrames.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Micro-Frontends with iFrames</title>
<style>
iframe {
width: 100%;
height: 500px;
border: none;
}
</style>
</head>
<body>
<h1>Micro-Frontend Example with iFrames</h1>
<!-- iFrames for Micro-Frontends -->
<iframe src="https://cart.example.com" id="cart-app"></iframe>
<iframe src="https://profile.example.com" id="profile-app"></iframe>
<iframe src="https://search.example.com" id="search-app"></iframe>
</body>
</html>
Step 3: Communication Between Host and Micro-Frontends
Since iFrames are isolated, communication happens via postMessage API or query parameters in the iFrame's URL.
Using postMessage for Communication
- Host to iFrame:
const cartIframe = document.getElementById('cart-app');
cartIframe.contentWindow.postMessage({ action: 'updateCart', data: { items: 5 } }, 'https://cart.example.com');
- iFrame to Host: In the micro-frontend application:
window.parent.postMessage({ action: 'cartUpdated', data: { total: 200 } }, '*');
- Listening for Messages: Host application listens for events:
window.addEventListener('message', (event) => {
if (event.origin === 'https://cart.example.com') {
console.log('Message from Cart App:', event.data);
}
});
Step 4: Deployment
Deploy each micro-frontend to its respective domain or subdomain. Ensure that:
- CORS policies allow communication between the host and iFrames.
- Security headers like
Content-Security-Policyare set correctly.
4. Enhancing the iFrame Micro-Frontend Experience
- Dynamic Loading of iFrames: Load iFrames dynamically based on user interaction to improve initial load performance.
function loadIframe(id, url) {
const iframe = document.getElementById(id);
if (!iframe.src) {
iframe.src = url;
}
}
loadIframe('cart-app', 'https://cart.example.com');
- Shared Styles and Branding: Use shared CSS or a centralized design system to ensure consistency across micro-frontends.
- Handling Navigation: Synchronize navigation between the host and iFrames using
postMessage.
5. Use Cases for iFrame-Based Micro-Frontends
- Legacy Systems: When integrating older systems into a new architecture.
- Security-First Applications: Applications requiring strict isolation, such as banking or healthcare systems.
- Diverse Frameworks: When micro-frontends use incompatible or outdated technologies.
6. Alternatives to iFrames
If iFrames do not meet your needs, consider using:
- Module Federation: Dynamically load and share code between micro-frontends.
- Web Components: Use framework-agnostic reusable components.
- Single-Spa: A framework for orchestrating micro-frontends.
Using iFrames for micro-frontends is a straightforward and robust method, especially for systems requiring strong isolation or integration of legacy apps.
Challenges and Considerations
While iFrames are useful, there are some challenges:
- Performance Issues: iFrames may slow down the host application if the legacy system is resource-intensive.
- Security Concerns: Cross-site scripting (XSS) or improper CORS configurations can pose risks.
- Communication Limitations: Parent and iFrame applications need mechanisms like the postMessage API for inter-application communication.
Conclusion
iFrames are a powerful tool for integrating legacy systems because they provide an isolated, framework-independent, and quick solution that requires no major changes to the legacy application. Despite their limitations, they remain a practical choice for embedding old systems into modern applications while maintaining functionality.
Micro-Frontend Architectures with Qiankun and Piral
Micro-frontends are an architectural style that allows multiple applications to work together seamlessly, often by integrating smaller, independently developed frontend applications into a cohesive user experience. Two popular frameworks for achieving this are Qiankun and Piral. Here's how you can use these tools for micro-frontend development:
1. Qiankun for Micro-Frontends
Qiankun is a micro-frontend framework based on Single-SPA. It simplifies the process of integrating multiple applications with features like sandboxing, isolation, and lifecycle management.
Key Features
- Framework Agnostic: Supports React, Vue, Angular, and other frameworks.
- Dynamic Loading: Applications can be loaded dynamically at runtime.
- Sandboxing: Provides style and script isolation to prevent conflicts.
Architecture Overview
- Main Application (Master): Manages routing and loads micro-frontends.
- Sub Applications (Micro-Frontends): Independently developed and deployed apps.
Steps to Implement with Qiankun
Step 1: Install Qiankun
npm install qiankun
Step 2: Create the Main Application
import { registerMicroApps, start } from 'qiankun';
// Register micro-frontends
registerMicroApps([
{
name: 'cart-app',
entry: 'https://cart.example.com',
container: '#container',
activeRule: '/cart',
},
{
name: 'profile-app',
entry: 'https://profile.example.com',
container: '#container',
activeRule: '/profile',
},
]);
// Start Qiankun
start();
Step 3: Setup Sub Applications Each micro-frontend should export lifecycle methods (bootstrap, mount, unmount):
export async function bootstrap() {
console.log('Micro-frontend bootstrapped');
}
export async function mount() {
console.log('Micro-frontend mounted');
}
export async function unmount() {
console.log('Micro-frontend unmounted');
}
Step 4: Communication Between Apps Qiankun provides an event bus for communication:
// Emit an event in one app
window.dispatchEvent(new CustomEvent('cartUpdated', { detail: { items: 5 } }));
// Listen for events in another app
window.addEventListener('cartUpdated', (event) => {
console.log('Cart updated:', event.detail);
});
Step 5: Deployment Deploy each micro-frontend independently, ensuring proper CORS and routing configurations.
2. Piral for Micro-Frontends
Piral is another powerful framework for building micro-frontends. It provides a modular architecture using Pilets (individual micro-frontends).
Key Features
- Pluggable Architecture: Add or remove features dynamically.
- Lazy Loading: Load micro-frontends as needed.
- Shared Resources: Share libraries and resources across micro-frontends.
Architecture Overview
- Shell Application: Hosts the micro-frontends and provides shared services.
- Pilets: Independent micro-frontends that plug into the shell.
Steps to Implement with Piral
Step 1: Install Piral
npm install piral-cli
Step 2: Create the Shell Application
piral new my-app-shell
Step 3: Add Pilets
pilet new --source my-app-shell
Step 4: Configure Communication Use the global state container or events for communication:
// Shell app: provide shared state
export function createSharedState() {
const state = createState({
cartItems: 0,
});
return state;
}
// Pilet: access shared state
const { cartItems } = useGlobalState();
Step 5: Deployment Deploy the shell and Pilets to a hosting service. The shell dynamically fetches and loads Pilets based on configuration.
Comparison: Qiankun vs Piral
| Feature | Qiankun | Piral |
|---|---|---|
| Framework Support | Framework-agnostic | Framework-agnostic |
| Integration Approach | Centralized registration | Decentralized pluggable modules |
| Communication | Custom events or props | Shared state or events |
| Dynamic Loading | Built-in | Built-in |
| Use Cases | Large-scale apps with strong isolation | Modular apps with shared resources |
Alternatives
- Single-SPA: A lightweight solution for routing and managing micro-frontends.
- Module Federation (Webpack): Share modules across micro-frontends at build and runtime.
- Web Components: Framework-independent components.
Conclusion
Both Qiankun and Piral offer robust solutions for implementing micro-frontends, each catering to different architectural needs. Qiankun is better suited for strict isolation and sandboxing, while Piral excels in modularity and shared resources. Choose the one that aligns with your application's requirements and team's expertise.
Final Conclusion
1. Web Components
Web Components are a native browser feature for creating reusable components that work across different frameworks and technologies.
When to Use Web Components:
- Framework Agnosticism: If you want true framework-agnostic components that can be used across different frameworks (React, Angular, Vue, etc.) without relying on any external libraries or microfrontend solutions.
- Isolated Components: When you need to create fully isolated, reusable components that encapsulate their styles and behavior.
- Simple Microfrontends: If your microfrontends consist of simple, reusable components and you want to avoid the overhead of a full microfrontend framework.
Pros:
- Can be used in any framework (React, Angular, Vue, etc.).
- No additional libraries or frameworks required.
- Strong encapsulation of styles and behavior.
Cons:
- Not ideal for managing complex microfrontends (e.g., managing routing, lifecycle, state, etc.).
- Might require polyfills for browser compatibility in older browsers.
2. Single SPA
Single SPA is a framework for integrating multiple microfrontends into a single page, allowing you to combine independent applications into one unified user experience.
When to Use Single SPA:
- Flexibility & Customization: If you need full control over your microfrontend architecture (e.g., lifecycle management, state management, routing).
- Multiple Independent Apps: When your microfrontends are independent and could be built with different technologies (React, Angular, Vue, etc.).
- Existing Apps: If you have a set of independent apps and want to gradually migrate them into a unified frontend using a microfrontend approach.
Pros:
- Full flexibility and control over each microfrontend.
- Allows integration of independent applications, which can be written with different technologies.
- Strong community and documentation.
Cons:
- Steeper learning curve.
- Requires manual setup for routing, lifecycle, and communication between apps.
- Requires more work on state management and communication.
3. Piral
Piral is a higher-level microfrontend framework built on top of single-spa. It provides a more structured approach for integrating microfrontends (called "pilets") into an application shell.
When to Use Piral:
- Structured Solution: If you want a more opinionated solution with built-in tools for state management, routing, and lifecycle management.
- Quick Setup: If you want to get microfrontends running quickly without worrying about manually managing configurations for state, routing, and lifecycle.
- Team Collaboration: If you’re working in a team and need clear separation of concerns, with features like automatic dependency management and shared state.
Pros:
- More opinionated and easier to use than Single SPA.
- Built-in state management and communication between microfrontends.
- Tools for easy integration and deployment of microfrontends.
- Great for larger applications with multiple teams.
Cons:
- Less flexibility than Single SPA.
- May have more overhead for smaller, simpler projects.
- Less control over certain aspects (e.g., lifecycle management).
4. Module Federation (Webpack 5)
Module Federation allows sharing code dynamically between different frontend applications, essentially enabling the sharing of components, libraries, or other resources.
When to Use Module Federation:
- Code Sharing: If you need to share code (e.g., React components, utility libraries) between different applications dynamically without redeploying the entire app.
- Microfrontends in a Monorepo: If you have a monorepo setup or need tight integration between various apps.
- Tightly Coupled Apps: When the microfrontends are not completely independent but need to share dependencies or code between apps.
Pros:
- Great for sharing code between microfrontends without duplication.
- Works well with React, Angular, or any other framework.
- Supports dynamic loading and code splitting for optimized performance.
Cons:
- Requires configuration and understanding of Webpack and Module Federation.
- More suitable for closely related microfrontends that share a lot of code.
5. Qiankun
Qiankun is a microfrontend framework that builds on top of single-spa, simplifying microfrontend integration by handling routing, shared dependencies, and lifecycle management.
When to Use Qiankun:
- Out-of-the-Box Microfrontend Solution: If you want a high-level solution that provides lifecycle management, dynamic loading of applications, and dependency sharing with minimal configuration.
- Framework-Agnostic Microfrontends: If you need a solution that integrates applications built with different frameworks (React, Vue, Angular) and want a more opinionated approach.
- Large-scale Applications: If your application consists of multiple teams working on separate apps that need to be integrated seamlessly.
Pros:
- Easier to use than Single SPA due to its abstraction and opinionated approach.
- Automatically handles lifecycle, routing, and dependency sharing.
- Built-in performance optimizations (e.g., preloading microfrontends).
- Supports dynamic loading and hot-reloading of microfrontends.
Cons:
- Less flexibility than Single SPA; less manual control.
- May have some limitations if you need very specific configurations or customizations.
Decision Matrix
| Use Case | Web Component | Single SPA | Piral | Module Federation | Qiankun |
|---|---|---|---|---|---|
| Simple, reusable components | ✔ | ❌ | ❌ | ❌ | ❌ |
| Integration of independent apps | ❌ | ✔ | ✔ | ✔ | ✔ |
| Quick setup with opinionated tooling | ❌ | ❌ | ✔ | ❌ | ✔ |
| Full control over routing & lifecycle | ❌ | ✔ | ✔ | ❌ | ✔ |
| Sharing code between microfrontends | ❌ | ❌ | ❌ | ✔ | ✔ |
| Integration with multiple frameworks | ✔ | ✔ | ✔ | ✔ | ✔ |
| Gradual migration (from monolithic to micro) | ❌ | ✔ | ✔ | ✔ | ✔ |
| State management & communication | ❌ | ⚙ | ✔ | ❌ | ✔ |
✔: The option meets the criteria completely or is the best choice without further adjustments.
X: The option does not meet the criteria or is not a viable choice for this criterion.
⚙: The option is partially suitable and requires additional setup, adjustments, or considerations to meet the criteria.
Recommendations
- Use Web Components if:
- You need reusable, independent components that can be used across different apps and frameworks.
- You have simple microfrontend needs without complex routing, lifecycle, or communication requirements.
- Use Single SPA if:
- You need flexibility and control over the integration of microfrontends and are comfortable configuring routing, lifecycle, and communication.
- You have a set of independent apps that need to be gradually integrated into a larger app.
- Use Piral if:
- You want a more structured solution with built-in state management, lifecycle management, and routing for large-scale microfrontends.
- You need to quickly set up microfrontends in a modular, collaborative environment.
- Use Module Federation if:
- Your microfrontends need to share dependencies or code (e.g., React components) dynamically and without duplication.
- You are working in a tightly coupled environment or with a monorepo.
- Use Qiankun if:
- You need a high-level, opinionated solution for integrating microfrontends with automatic handling of routing, lifecycle, and shared dependencies.
- You have multiple apps that need to be integrated smoothly, especially when those apps are built using different technologies.
Ultimately, the choice depends on project requirements, team experience, and how much control we need over the microfrontend architecture. If we want more structure and less configuration, Piral or Qiankun could be ideal. If we prefer full flexibility, Single SPA or Module Federation might be better suited for your needs. Or if it’s only simple component then Web Component will be better.
The explanation provides guidance on choosing the appropriate microfrontend tool:
1. "If we want more structure and less configuration, Piral or Qiankun could be ideal."
- Why?
- Piral and Qiankun are frameworks specifically designed for microfrontends, offering pre-built tools, patterns, and conventions to simplify development. They help enforce a structured architecture by managing routing, state sharing, and communication between microfrontends.
- These tools focus on reducing the effort required for setup and configuration, making them ideal for teams that want to focus on building features rather than managing complex architecture.
- Example Use Case: A large enterprise with multiple teams working on different parts of a single application might benefit from this structured approach to ensure consistency and reduce development overhead.
2. "If we prefer full flexibility, Single SPA or Module Federation might be better suited for your needs."
- Why?
- Single SPA and Webpack Module Federation give you full control over the microfrontend architecture, including routing, communication, and deployment strategies. They act more like tools rather than full-fledged frameworks.
- This flexibility is great for teams with experience in managing microfrontend architecture and who have specific requirements that don't fit into the rigid structure of frameworks like Piral or Qiankun.
- However, this flexibility comes at the cost of more configuration and potentially higher complexity in managing the system.
- Example Use Case: A tech-savvy team that wants to experiment with different frameworks or has unique requirements (e.g., mixing different JavaScript frameworks like Angular, React, and Vue) might prefer this approach.
3. "Or if it’s only a simple component, then Web Component will be better."
- Why?
- Web Components are lightweight and framework-agnostic, making them ideal for building simple, reusable UI elements like buttons, dropdowns, or modals.
- Unlike microfrontend frameworks (Piral, Qiankun, or Single SPA), Web Components are not meant to manage large-scale applications or handle inter-component communication.
- They are perfect when you only need to create isolated, self-contained components that can be reused across different projects or frameworks.
- Example Use Case: A small component like a custom date picker or a styled input box that needs to be shared across multiple applications.
Note:
- Piral or Qiankun: Choose when you need structure, predefined conventions, and minimal configuration for managing multiple microfrontends.
- Single SPA or Module Federation: Choose when you need full control, flexibility, and are comfortable with configuring the microfrontend architecture yourself.
- Web Components: Choose when you only need simple, reusable, and framework-independent components.