Micro-Frontend Architecture (MFE) is a modern web development pattern that extends the principles of microservices to the frontend. In MFE, a larger web application is divided into smaller, self-contained “micro-frontends,” which are individual, independently developed, tested, and deployed pieces. Each micro-frontend focuses on a specific feature or functionality and can be built by different teams using different technology stacks.
Key Concepts of MFE
- Independence: Each micro-frontend is an independent module that can be developed and deployed separately.
- Team Autonomy: Teams can work on their micro-frontends without affecting others, allowing for faster development cycles.
- Technology Diversity: Different micro-frontends can use different libraries, frameworks, or versions, although this is generally used sparingly due to performance and maintainability concerns.
Options for Creating MFEs in a React Application
Several approaches can be used to create micro-frontends in a React application. Each approach has its own use cases, benefits, and limitations:
1. Webpack Module Federation
- Description: Webpack’s Module Federation feature, introduced in Webpack 5, allows applications to share code and load modules at runtime. With Module Federation, a React application (host) can import components, functions, or even entire applications from another independently deployed React application (remote).
- How it Works: Each micro-frontend exposes specific modules, which can then be dynamically imported by other applications without redeploying them. Webpack manages the dependency resolution, ensuring that shared libraries (e.g.,
React) are loaded only once. - Use Case: Recommended for applications that require real-time integration of different modules without bundling all modules into one large application at build time. Ideal for complex enterprise applications where individual teams need to manage separate parts of the frontend.
- Pros:
- Runtime loading of components
- Reduced redundancy by sharing libraries like React
- Version conflict resolution capabilities
- Cons:
- Dependency on Webpack 5
- Potential for version conflicts in shared libraries, if not well managed
- Complexity in coordinating shared dependencies
// Example: Exposing a Button component from one application to another with Module Federation
new ModuleFederationPlugin({
name: "app1",
filename: "remoteEntry.js",
exposes: {
"./Button": "./src/Button",
},
shared: ["react", "react-dom"],
});
2. Single-SPA (Single Single Page Application)
- Description: Single-SPA is an open-source framework for creating MFEs by combining multiple JavaScript frameworks into one application. Single-SPA allows different frameworks (e.g., React, Angular, Vue) to coexist on the same page, enabling different micro-frontends to load separately.
- How it Works: Single-SPA orchestrates the loading, mounting, and unmounting of each micro-frontend based on route changes or other custom criteria. Each micro-frontend registers with the Single-SPA root config, which then loads or unloads the micro-frontend as needed.
- Use Case: Ideal when a team needs to migrate a monolithic application to a micro-frontend architecture gradually or when different frameworks need to coexist.
- Pros:
- Supports multiple frameworks
- Suitable for gradual migration from a monolithic to MFE
- Clear lifecycle management for each micro-frontend
- Cons:
- Higher initial setup complexity
- More dependencies, as it requires additional libraries for routing and lifecycles
- Potential performance overhead due to multiple frameworks
// Example: Registering a React app in a Single-SPA config
import { registerApplication, start } from "single-spa";
registerApplication({
name: "@org/react-app",
app: () => System.import("@org/react-app"),
activeWhen: ["/react"],
});
start();
3. iframe-based Micro-Frontends
- Description: In this approach, each micro-frontend runs inside an
<iframe>. Each iframe can load a separate application hosted on its own domain or subdomain, making this approach a quick and straightforward way to integrate multiple frontends. - How it Works: The main application embeds multiple iframes for each micro-frontend, which can communicate with the host or with each other using the
postMessageAPI. - Use Case: Often used in cases where isolation and security are essential, or when quick integration of legacy applications is needed.
- Pros:
- Strong isolation between applications
- Simplified dependency management (each iframe has its own isolated environment)
- Easy to integrate legacy applications
- Cons:
- Limited ability to share global state between micro-frontends
- Increased resource usage due to multiple browser contexts
- Difficult to achieve a seamless UX due to limitations of cross-iframe communication
<!-- Example: Embedding two micro-frontends in iframes -->
<iframe src="http://microfrontend1.com" title="Micro-Frontend 1"></iframe>
<iframe src="http://microfrontend2.com" title="Micro-Frontend 2"></iframe>
4. Server-Side Composition
- Description: In server-side composition, the server assembles micro-frontends into a single HTML response. This approach uses techniques like edge-side includes (ESI) or server-side rendering (SSR) to fetch and assemble fragments from each micro-frontend service.
- How it Works: Each micro-frontend is rendered on the server, and the server assembles the responses into a single HTML page, which is then sent to the client.
- Use Case: Useful for applications with strong SEO requirements or for content-heavy applications where initial load time is critical.
- Pros:
- Excellent for SEO and initial load performance
- Good isolation between micro-frontends
- Cons:
- Can be more challenging to set up
- Difficult to achieve fast interactivity, as updates require page reloads or dynamic loading
// Example: A server using ESI to include fragments
app.get("/", (req, res) => {
res.send(`
<html>
<body>
<esi:include src="http://microfrontend1.com/content" />
<esi:include src="http://microfrontend2.com/content" />
</body>
</html>
`);
});
5. Build-time Integration with Lerna or Yarn Workspaces
- Description: Lerna and Yarn Workspaces allow mono-repo setups where multiple packages are managed in a single repository. Each package could represent a micro-frontend, and packages can share dependencies within the same repo.
- How it Works: Each micro-frontend is created as a package, and the main application (or host) imports the packages during the build process, allowing shared dependencies to be deduplicated.
- Use Case: Best for teams that prefer a mono-repo structure and want all micro-frontends to be built and deployed together.
- Pros:
- Simplified dependency management
- Great for closely related micro-frontends
- Cons:
- No true independent deployment of micro-frontends
- Larger builds, as all micro-frontends are compiled together
// Example: Package.json configuration with Yarn Workspaces
{
"workspaces": [
"packages/microfrontend1",
"packages/microfrontend2"
]
}
Comparison Summary
| Approach | Pros | Cons | Use Cases |
|---|---|---|---|
| Webpack Module Federation | Dynamic runtime sharing, high modularity | Webpack dependency, potential version conflicts | Enterprise applications needing separate deploys |
| Single-SPA | Multi-framework support, gradual migration | Complexity, performance overhead | Gradual migration, multiple frameworks in one app |
| iframe-based | Strong isolation, easy legacy integration | Performance overhead, limited UX control | Isolated systems, legacy integration |
| Server-side Composition | SEO, fast initial load | Setup complexity, limited interactivity | SEO-driven applications |
| Lerna/Yarn Workspaces | Simplified dependency management | Limited deploy independence | Mono-repo setups, close-knit micro-frontends |
Conclusion
Each method has specific strengths and weaknesses, and the best choice depends on factors like application complexity, need for independence, SEO considerations, and team structure. Webpack Module Federation is a popular and flexible choice for dynamic React applications, while Single-SPA and iframes offer unique advantages for multi-framework or legacy environments.
