Learn when and why to split a frontend into microfrontends, how Webpack Module Federation works, how Single-SPA compares, and how to design a microfrontend architecture that actually scales — all the questions interviewers ask seniors about frontend composition.
Microfrontends apply the microservices pattern to the UI. Instead of one giant React app deployed by one team, you split the frontend into independently deployable vertical slices — each owned by a team that controls everything from database to UI. The browser stitches these pieces together at runtime or build time, like assembling a page from Lego blocks where each block is a self-contained app.
In large organisations, a single frontend monolith becomes a bottleneck: 20 teams all commit to the same repo, CI takes 30 minutes, one bad deploy blocks everyone. Microfrontends decouple teams so they can build, test, and deploy independently — exactly like microservices did for backends.
Webpack 5's built-in mechanism for sharing code at runtime across separate builds. One app exposes components; another app consumes them — no npm publishing required. The host app downloads the remote bundle lazily when needed.
// Remote (Team B — Cart MFE) — webpack.config.js
new ModuleFederationPlugin({
name: 'cart',
filename: 'remoteEntry.js',
exposes: {
'./CartWidget': './src/CartWidget',
},
shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
})
// Host (Shell App) — webpack.config.js
new ModuleFederationPlugin({
name: 'shell',
remotes: {
cart: 'cart@https://cart.example.com/remoteEntry.js',
},
shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
})
// Shell — usage at runtime
const CartWidget = React.lazy(() => import('cart/CartWidget'));
// ↑ loads from cart.example.com at runtime, not bundled into shell
Key insight: shared: { react: { singleton: true } } ensures both apps use the same React instance — critical to avoid the "two Reacts" bug.
A JavaScript framework for orchestrating multiple micro-apps. Each app registers with a root-config that controls when to mount/unmount them based on the URL. Unlike Module Federation, Single-SPA is framework-agnostic — you can mix React, Vue, and Angular in the same shell.
// Root config — registers microfrontends
registerApplication({
name: '@company/navbar',
app: () => import('@company/navbar'),
activeWhen: ['/'], // always active
});
registerApplication({
name: '@company/products',
app: () => import('@company/products'),
activeWhen: ['/products'],
});
Strongest isolation — each microfrontend is sandboxed in its own browsing context. Zero risk of CSS or JS conflicts. But terrible UX: poor accessibility, fixed height issues, hard to share state, navigation breaks browser history.
Native custom elements that can encapsulate framework-specific UI. A React microfrontend wrapped in a Web Component can be dropped into any HTML page. Good for widgets and embeds, not great for complex composed applications.
Microfrontends that need shared state (user session, cart count, notifications) should communicate through events not direct references. The shell owns global state; MFEs fire custom events or use a lightweight event bus. Direct imports between MFEs create hidden coupling and break independent deployability.
// Event bus pattern for cross-MFE communication
window.dispatchEvent(new CustomEvent('cart:updated', { detail: { count: 3 } }));
// Another MFE listens
window.addEventListener('cart:updated', (e) => setCount(e.detail.count));
Small teams and small apps do not benefit — you get all the operational complexity with none of the team-scaling benefit. The overhead of separate repos, CI pipelines, versioning, and bundle size management is only justified when multiple independent teams work on the same product simultaneously.
Microfrontends mean different frameworks — you can have a React-only microfrontend architecture. Framework diversity is a capability, not a requirement, and usually increases complexity.
Module Federation replaces npm packages — MF is for runtime-loaded UI components, not shared utilities. Shared logic (utils, hooks) should still be published as internal npm packages.
Each microfrontend should have its own design system — shared UI components should come from a single design system package to ensure visual consistency across teams.
Microfrontends are always faster — having 5 separate bundles that all load React independently is often slower. The `shared: singleton` config and careful chunk splitting are needed to avoid duplicate dependencies.
Spotify: different teams own the music player, podcast section, and search — each deployed independently. The shell composes them at runtime.
IKEA: product pages, cart, and checkout are separate deployable frontends that share a design system but are owned by different teams
Amazon: their product page is notoriously composed of hundreds of independent services rendering HTML that is assembled server-side — a server-driven MFE pattern
Large enterprise portals: a financial services platform might have trading, analytics, and portfolio teams each owning their screen within one shell application
What is a microfrontend and what problem does it solve?
How does Webpack Module Federation work?
What are the main challenges of microfrontends in production?
How do you share state between microfrontends?
When should you NOT use microfrontends?
What is Single-SPA and how does it differ from Module Federation?
Reading answers is not the same as knowing them. Practice saying them out loud with AI feedback — that's what builds real interview confidence.