Ways to Create a Seamless Application for Multiple Standalone Microfrontends
As modern web applications scale, many teams are turning to microfrontend architecture to split large apps into smaller, manageable, and independently deployable units. However, serving standalone apps (like with NGINX) is only half the battle—making them work seamlessly together as one cohesive user experience requires thoughtful integration.
This guide explores the top strategies to stitch together multiple microfrontends into a single, unified application.
1. Single-SPA: The Microfrontend Orchestrator
Single-SPA is a powerful JavaScript framework for orchestrating multiple frameworks or libraries as microfrontends.
🛠 How It Works:
- You build each app independently (e.g.,
/app1,/app2). - A root-config app manages which one to load based on the route.
- Uses SystemJS or native
import()to load apps dynamically.
Example:
registerApplication({
name: "@org/app1",
app: () => System.import("@org/app1"),
activeWhen: ["/app1"],
});
registerApplication({
name: "@org/app2",
app: () => System.import("@org/app2"),
activeWhen: ["/app2"],
});
start();
Pros:
- Route-based activation
- Supports multiple frameworks (React, Vue, Angular)
- Rich ecosystem and documentation
2. Module Federation (Webpack 5)
Module Federation allows apps to share code and load each other at runtime.
🛠 How It Works:
- Apps expose components via
ModuleFederationPlugin. - Other apps consume them as
remotes. - Great for sharing dependencies like React, styles, and state.
Example:
App1 Webpack Config
plugins: [
new ModuleFederationPlugin({
name: "app1",
filename: "remoteEntry.js",
exposes: {
"./App": "./src/App",
},
shared: ["react", "react-dom"],
}),
];
Pros:
- Shared dependencies = faster load
- Dynamic import = performance benefits
- Works well with CI/CD and versioning
3. Custom Router + JavaScript Runtime Integration
You can manually load microfrontend apps using <script> tags and mount them into specific DOM nodes.
Example:
<div id="app1-root"></div>
<script src="/app1/main.js"></script>
<script>
window.App1.mount(document.getElementById("app1-root"));
</script>
Pros:
- Simple and flexible
- No build tool coupling
Cons:
- You must define a global
mount()function - Managing shared state and dependencies gets tricky
4. iframe-based Isolation (Optional for Admin Tools or Legacy Apps)
Embedding microfrontends inside <iframe>s is the most isolated approach.
<iframe src="/app1" width="100%" height="800px"></iframe>
Pros:
- Full isolation (CSS, JS, state)
- Safe for untrusted apps
Cons:
- Navigation and deep linking are hard
- Poor performance and UX
- Not ideal for seamless integration
5. Import Maps and CDN-shared Libraries
You can serve shared dependencies (like React, Vue, etc.) via a CDN or importmap, reducing duplication and ensuring consistency.
HTML Example:
<script type="importmap">
{
"imports": {
"react": "https://cdn.skypack.dev/react",
"react-dom": "https://cdn.skypack.dev/react-dom"
}
}
</script>
Pros:
- Smaller bundles
- Shared cache across apps
NGINX Setup: Serving Standalone Microfrontends
If you’re serving apps like /app1, /app2, etc., a typical nginx.conf might look like:
location = /app1 {
return 301 https://$host/app1/;
}
location /app1/ {
try_files $uri $uri/ /app1/index.html;
}
location /app1/assets/ {
alias /var/www/apps/app1/assets/;
}
For seamless routing across apps, use a fallback rewrite rule:
location / {
try_files $uri $uri/ @microfrontends;
}
location @microfrontends {
rewrite ^/app1(/.*)?$ /app1/index.html break;
rewrite ^/app2(/.*)?$ /app2/index.html break;
}
Best Practices for Seamless Integration
| Area | Recommendation |
|---|---|
| Routing | Centralize routing in a root-config app |
| State Sharing | Use federated state management (Redux, Zustand) |
| Styling | Namespace CSS or use Shadow DOM |
| Auth | Shared authentication and session management |
| Navigation | Provide a global navigation shell or menu |
Conclusion
Serving multiple standalone microfrontends is just the first step. To make them feel like one application, you need:
- A shared orchestration layer
- Consistent routing and state
- Smooth user transitions
By using tools like Single-SPA or Webpack Module Federation, and proper server configurations, you can build a powerful, scalable microfrontend architecture that feels like a single product to your users.