As a Senior Frontend Architect, I've witnessed countless discussions about micro-frontends where teams mistakenly identify architectural patterns, leading to poor decisions and technical debt.
My Pain Point: The Tragedy of Misunderstood Architecture
Let me tell you a story that still haunts me. I was in a crucial architectural meeting where we were deciding the future of our frontend strategy. The lead architect confidently presented their "micro-frontend solution" – and I watched in horror as they described what was essentially a glorified component library.
"We'll create reusable components," they enthusiastically explained, "the Login component, the Dashboard component, the UserProfile component. Each team can own their components, and we'll have micro-frontends!"
Fortunately, I managed to catch the problem in time. After hours of discussion and presenting concrete examples, I was able to convince them of the true meaning of what a micro-frontend entails. We'll explore what that means later.
But that wasn't the worst of it. In another project, I witnessed pure madness: an architect seriously proposing to compose entire pages using multiple iframes. "We'll have the header iframe, the sidebar iframe, the main content iframe, and the footer iframe," they said with a straight face. "Each team can own its iframe!"
Thankfully, a few days later, they walked back their proposal entirely.
This article aims to prevent that pain by clearly defining what micro-frontends are NOT, so you can avoid these painful mistakes.
Let's clarify what micro-frontends truly are by examining what they are NOT.
What Micro-Frontends Are NOT
❌ NOT Just Multiple SPAs Behind a Reverse Proxy
Many developers think that having several Single Page Applications (SPAs) running on different ports and routing them through a reverse proxy creates a micro-frontend architecture. This is one of the most common misconceptions.
# This is NOT a micro-frontend architecture
server {
location /dashboard { proxy_pass http://react-dashboard:3000; }
location /profile { proxy_pass http://vue-profile:3001; }
location /billing { proxy_pass http://angular-billing:3002; }
}
Why it fails:
-
Full page reloads: When users navigate from
/dashboard
to/profile
, the entire page reloads, breaking the single-page application experience. - No shared state: User authentication, cart data, or any global state is lost between applications.
- Inconsistent UI: Each app loads its own CSS, fonts, and UI components, leading to visual flickering and inconsistent design.
- Performance issues: Each transition requires downloading new JavaScript bundles, CSS files, and fresh API calls.
- Browser history problems: Back button behavior becomes unpredictable across different applications.
Real-world analogy: It's like having separate websites pretending to be a single application – imagine if Gmail reloaded the entire page every time you switched from Inbox to Compose!
❌ NOT a Component Library or Design System
Another common confusion happens when developers think that using shared UI components from a design system equates to micro-frontends. While component libraries are valuable, they are fundamentally different from micro-frontend architecture.
// This is a component library, NOT a micro-frontend
import { Button, Modal, DataTable } from '@company/ui-components';
function UserManagement() {
return (
<div>
<Button onClick={handleCreate}>Create User</Button>
<DataTable data={users} />
</div>
);
}
Why it fails:
- Shared dependencies: All teams must use the same version of the component library, creating coordination overhead.
- No independent deployments: If the component library changes, all applications must update and redeploy.
- Single point of failure: A bug in the shared component affects every application that uses it.
- Limited team autonomy: Teams cannot make independent technology choices or significantly customize components.
- Not business-focused: Components are UI building blocks, not complete business functionalities.
Think of it this way: Component libraries are like LEGO bricks that everyone shares, but micro-frontends are like complete LEGO sets (spaceship, castle, car) that different teams build and can swap in and out of a larger city.
❌ NOT iframe-Based Solutions
While iframes technically allow embedding different applications, they create more problems than they solve and represent an outdated approach that many beginners mistakenly consider "micro-frontends."
<div class="app-container">
<iframe src="/dashboard-app" width="100%" height="500px"></iframe>
<iframe src="/analytics-app" width="100%" height="400px"></iframe>
</div>
Why it fails:
- Styling nightmare: Each iframe has its own CSS context, making it impossible to create consistent spacing, fonts, and colors across applications.
- Communication barriers: Parent and child applications struggle to share data or coordinate actions (like showing loading states).
- Accessibility issues: Screen readers and keyboard navigation break across iframe boundaries, failing users with disabilities.
- Performance bottlenecks: Each iframe loads a complete HTML document with its own CSS and JavaScript, consuming unnecessary resources.
- Poor mobile responsiveness: Iframes do not play well on mobile devices, leading to scrolling issues and touch interaction problems.
- SEO challenges: Search engines have difficulty correctly indexing content within iframes.
Real-world comparison: Using iframes is like having separate TV screens for each room in your house instead of having one smart TV that can display different content – technically possible but clunky and inefficient.
❌ NOT Monorepos with Multiple Apps
Many developers assume that organizing multiple applications within a single repository (monorepo) automatically creates a micro-frontend architecture. This is a fundamental misunderstanding of what micro-frontends represent.
{
"name": "company-apps",
"workspaces": [
"apps/dashboard",
"apps/admin",
"apps/mobile"
]
}
Why it fails:
- Deployment coupling: Even if apps are separate, they are often deployed together, defeating the purpose of independent releases.
- Shared build process: All applications typically use the same build tools, Node.js version, and deployment pipeline, limiting technology choices.
- Code organization ≠ Runtime integration: Having separate folders doesn't mean the applications work together as a unified experience.
- Missing runtime composition: Users still navigate between entirely separate applications rather than seamlessly integrated functionalities.
- Coordination overhead: Teams still need to coordinate changes, updates, and releases because everything lives in the same repository.
Simple analogy: A monorepo with multiple apps is like having separate apartments in the same building – they are organized together but are still completely independent living spaces. Micro-frontends are more like having different rooms in the same house that work together to create a single living experience.
❌ NOT Route-Based Code Splitting
This is probably the most subtle misconception. Many developers believe that splitting their application's code based on routes and dynamically loading them creates micro-frontends. While code splitting is an excellent optimization technique, it's not the same as micro-frontend architecture.
// This is code splitting, NOT micro-frontend
const Dashboard = lazy(() => import('./Dashboard'));
const Profile = lazy(() => import('./Profile'));
function App() {
return (
<Router>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile" element={<Profile />} />
</Routes>
</Router>
);
}
Why it fails:
- Single deployment unit: All code splitting happens within one application that is deployed as a single unit.
- Single team ownership: One team still owns the entire application, including all routes and functionalities.
- Shared dependencies: All route components use the same React version, same build tools, same everything.
- Build-time splitting: The splitting happens when you build the app, not when different teams deploy their functionalities.
- Technology lock-in: You can't have Dashboard in React and Profile in Vue – everything must use the same technology stack.
- Coordination required: Adding new routes or changing shared logic still requires coordination with the entire team.
Easy way to remember: Code splitting is like having a large closet with organized sections (shirts, pants, shoes) – it's still one closet owned by one person. Micro-frontends are like having separate wardrobes that different people own and manage, but they all contribute to a unified clothing experience.
What Micro-Frontends Really Are
Micro-frontends are independently deployable frontend applications that:
- Work together in a single page/browser context.
- Are owned by different teams with full autonomy.
- Are deployed independently without coordination.
- Are integrated at runtime using composition techniques.
✅ A True Micro-Frontend Example
// Shell application (container)
import { registerApplication, start } from 'single-spa';
registerApplication({
name: 'dashboard',
app: () => System.import('@company/dashboard'),
activeWhen: ['/dashboard']
});
registerApplication({
name: 'user-profile',
app: () => System.import('@company/user-profile'),
activeWhen: ['/profile']
});
start();
// Individual micro-frontend (dashboard team)
import React from 'react';
import ReactDOM from 'react-dom';
import singleSpaReact from 'single-spa-react';
const Dashboard = () => <div>Dashboard Content</div>;
const lifecycles = singleSpaReact({
React,
ReactDOM,
rootComponent: Dashboard
});
export const { bootstrap, mount, unmount } = lifecycles;
Key Indicators You're Doing Micro-Frontends Correctly
✅ Different teams can deploy independently.
✅ Applications seamlessly integrate at runtime.
✅ Teams can choose different technologies.
✅ Shared navigation and consistent UX.
✅ Applications can communicate when necessary.
When NOT to Use Micro-Frontends
- Small teams (< 3 frontend teams)
- Simple applications
- Tight coupling requirements
- Performance is hyper-critical
- Limited infrastructure resources
Conclusion
Micro-frontends solve organizational problems, not technical ones. They enable team autonomy and independent deployment at the cost of increased complexity. Before adopting them, ensure you have the team structure and organizational needs that justify the architectural overhead.
The key is to understand that micro-frontends are about runtime integration of independently deployed applications, not just splitting code or using multiple technologies.
The Definitive Definition
To close this discussion, I'll share the definition from Luca Mezzalira, one of the most respected voices in the micro-frontend community and author of "Building Micro-Frontends":
"Micro-frontends are the technical representation of a business subdomain. They allow independent implementation with the same or different technology. Finally, they should minimise the code shared and owned by other subdomains owned by a single team."
This definition perfectly captures the essence:
- Focus on business subdomain: Not technical functionalities, but complete business capabilities.
- Strong boundaries: Clear separation of concerns and responsibilities.
- Clear contracts: Well-defined interfaces for communication.
- No shared logic: True independence between different subdomains.
When evaluating whether you're building true micro-frontends, ask yourself: "Does this represent a complete business subdomain with its own team, deployment cycle, and clear boundaries?" If the answer is no, you're likely dealing with one of the anti-patterns we've discussed.
What's your experience with micro-frontends? Have you seen these anti-patterns in your organization? Share your thoughts in the comments below! 👇
Iframes that is blast form the past :)
I'm still skeptical about micro frontends. In the backend a micro-service can be a solution if you need to hyperscale. But modular architecture is for me a great way to prepare the scaling, and for most sites that is good enough.
What is the point for you to go from modular to micro frontends?
Micro-services can have multiple languages, but I prefer to not have a language per mico-service. A micro-service should require the strengths of a language.
With micro frontends i don't see the reason to introduce multiple view library runtimes because of teams.
In my backend mindset I want to send as little data to the browser as possible. This lets do servers more requests, and it is a performance win in the browser.