Microfrontends Interview Questions and Answers
18 hand-picked Microfrontends interview questions with
detailed answers. Open the interactive version above to search, filter
by difficulty, run code, bookmark questions and track your progress.
What are microfrontends and why do teams adopt them?
Microfrontends extend the microservices idea to the browser: a large web UI is split into smaller, independently owned front-end applications that compose into one product experience.
Each MFE is typically owned by a squad, has its own repo (or workspace package), build pipeline, and release cadence. A shell / host loads and orchestrates them at runtime or build time.
Teams adopt MFEs to scale delivery — multiple Angular squads can ship without blocking each other on a single monolithic SPA release.
How do microfrontends differ from a monolithic Angular SPA?
A monolithic SPA is one Angular workspace: one build, one deploy artifact, shared AppModule or standalone bootstrap, and lazy-loaded feature modules inside the same bundle graph.
Microfrontends are separate deployable apps. Each has its own build output; the shell stitches them together at runtime (Module Federation) or via an orchestrator (single-spa).
| Monolith SPA | Microfrontends |
|---|
| One repo / one pipeline | Multiple repos or packages, multiple pipelines |
| Shared DI tree by default | Separate Angular platforms; integration is explicit |
| Simpler routing & state | Cross-MFE routing and shared libs need design |
| One version of Angular | Version alignment becomes a governance topic |
What is the shell (host) vs a remote MFE in Angular?
The shell / host is the container application users land in first. It usually owns:
- Global layout, header, sidebar, and branding
- Authentication bootstrap and route guards
- Loading remote bundles and mounting them in the DOM
- Shared design system and cross-cutting concerns
A remote is a standalone Angular app exposed as a federated module or registered micro-app. It focuses on a domain (e.g. "Partner Onboarding") and is consumed by the host — it does not own the full page chrome.
// Host routes (conceptual)
{
path: 'catalog',
loadChildren: () =>
loadRemoteModule({
type: 'module',
remoteEntry: 'https://catalog.example.com/remoteEntry.js',
exposedModule: './CatalogRoutes'
}).then(m => m.CATALOG_ROUTES)
}
Explain Webpack Module Federation and how it works with Angular.
Module Federation (Webpack 5+, supported in Angular via @angular-architects/module-federation or Nx) lets a host dynamically import JavaScript from a remote at runtime.
The remote publishes a remoteEntry.js manifest. The host declares remotes in webpack.config / federation.config.js and loads exposed modules (routes, components, NgModules) on demand.
Shared dependencies (e.g. @angular/core) can be configured as singletons so only one copy runs in the browser.
// federation.config.js (remote)
module.exports = withModuleFederationPlugin({
name: 'catalog',
exposes: {
'./CatalogRoutes': './src/app/catalog.routes.ts',
},
shared: {
'@angular/core': { singleton: true, strictVersion: true, requiredVersion: 'auto' },
'@angular/router': { singleton: true, strictVersion: true, requiredVersion: 'auto' },
},
});
What is single-spa and when would you use it over Module Federation?
single-spa is a framework-agnostic orchestrator. It registers multiple micro-apps (Angular, React, Vue) and mounts/unmounts them based on URL activity functions — not tied to Webpack.
Each Angular app is wrapped with single-spa-angular, exposing bootstrap/mount/unmount lifecycles. A root config decides which app is active for a route.
Use single-spa when you need multi-framework composition or a central runtime router across heterogeneous apps. Prefer Module Federation when all squads are Angular and you want native lazy route integration with less wrapper code.
// single-spa root config (simplified)
registerApplication({
name: '@org/catalog',
app: () => System.import('https://cdn.example.com/catalog/main.js'),
activeWhen: ['/catalog'],
});
How do you manage shared dependencies and Angular version alignment across MFEs?
Shared libraries (@angular/*, RxJS, your design system) must be singletons at runtime — duplicate Angular cores break DI, change detection, and routing.
Strategies:
- Aligned versions — pin the same Angular minor across host and remotes (recommended for 0–4 YOE teams).
- Module Federation shared config —
singleton, strictVersion, requiredVersion. - Internal npm feed — publish
@company/ui-kit and @company/auth as versioned packages. - Monorepo (Nx) — one lockfile, affected builds, consistent deps with federated deploy targets.
shared: {
'@angular/core': { singleton: true, strictVersion: true, requiredVersion: '^19.0.0' },
'@company/design-system': { singleton: true, requiredVersion: '^2.4.0' },
rxjs: { singleton: true, strictVersion: false },
}
How does routing work across microfrontends in an Angular shell?
The host owns the top-level router. Each route segment delegates to a remote's child routes via dynamic loadChildren / loadRemoteModule.
Pattern: host defines /catalog/** → loads Catalog remote routes; /orders/** → Order remote. Remotes export a Routes array with paths relative to their mount point.
Deep links and browser back/forward must work — URL is the contract between teams. Avoid remotes registering conflicting root paths.
// Host app.routes.ts
export const APP_ROUTES: Routes = [
{ path: '', component: ShellLayoutComponent, children: [
{ path: 'catalog', loadChildren: () => loadRemoteModule({...}).then(m => m.CATALOG_ROUTES) },
{ path: 'orders', loadChildren: () => loadRemoteModule({...}).then(m => m.ORDER_ROUTES) },
]},
];
How can microfrontends communicate without tight coupling?
Prefer loose integration over shared mutable singletons when domains are independent.
- Custom events —
window.dispatchEvent(new CustomEvent('cart:item-added', { detail })); other MFEs subscribe. Framework-agnostic and boundary-friendly. - URL / query params — share context via router state (bookmarkable, debuggable).
- Shared event bus service — thin RxJS
Subject in a federated shared lib (use sparingly). - Backend as source of truth — MFEs sync via APIs/WebSockets, not in-memory cross-app state.
// Publisher (Catalog MFE)
window.dispatchEvent(new CustomEvent('nc:product-selected', {
detail: { productId: 'SKU-42' },
bubbles: true,
}));
// Subscriber (Order MFE)
window.addEventListener('nc:product-selected', (e: Event) => {
const { productId } = (e as CustomEvent).detail;
});
Can NgRx be shared across Angular microfrontends? What are the pitfalls?
Technically yes — provide a shared Store via a federated singleton library — but it's risky for loosely coupled domains.
Better patterns:
- Shell store for true cross-cutting state — auth user, tenant, theme, locale only.
- Feature stores inside each remote — catalog slice lives in catalog MFE.
- Event-driven sync — custom events or message bus instead of one giant global store.
Pitfalls: remotes importing each other's selectors creates hidden coupling; lazy-loaded remotes may register duplicate feature keys if not namespaced.
// Shared lib (federated singleton)
export const sharedStoreProviders = [
provideStore({ session: sessionReducer }),
provideState('session', sessionReducer),
];
// Host bootstrap
bootstrapApplication(AppComponent, {
providers: [...sharedStoreProviders, provideRouter(APP_ROUTES)],
});
How does independent deployment work for microfrontends?
Each MFE has its own CI/CD pipeline producing its own artifacts (e.g. remoteEntry.js + chunks) deployed to a unique URL or CDN path.
The host references remotes by URL from environment config or a runtime manifest service — so a catalog squad can deploy v2.3 without rebuilding the shell.
Guardrails: contract tests, shared lib semver, and canary URLs. Rollback is per-MFE. Coordinate breaking changes to exposed modules via API versioning (./CatalogRoutesV2).
// environment.prod.ts (host)
export const environment = {
remotes: {
catalog: 'https://cdn.example.com/catalog/v2.3/remoteEntry.js',
orders: 'https://cdn.example.com/orders/v1.8/remoteEntry.js',
},
};
How do you prevent CSS conflicts between microfrontends?
Angular's default emulated encapsulation scopes component styles, but global stylesheets and third-party CSS can still leak across MFEs loaded into one page.
- Design system with prefixed utility classes —
nc-btn-primary namespace. - Shadow DOM (
ViewEncapsulation.ShadowDom) for high-risk widgets. - CSS Modules / BEM in shared libs.
- Strict rule: no global
styles.css overrides in remotes except tokens. - iframes — strongest isolation, worst UX/integration cost (use only when necessary).
@Component({
selector: 'app-risky-widget',
encapsulation: ViewEncapsulation.ShadowDom,
template: `<button class="primary">Buy</button>`,
styles: [`:host { display: block; } .primary { ... }`],
})
How do Angular standalone components and routes fit into a microfrontend architecture?
Modern Angular MFEs favor standalone bootstraps — no NgModule shell in remotes. A remote typically:
- Bootstraps locally for dev (
bootstrapApplication). - Exports
Routes or standalone components via Module Federation for the host.
Production remotes often skip full self-bootstrap and expose export const CATALOG_ROUTES: Routes = [...] consumed by the host's provideRouter tree — cleaner than sharing NgModules.
// catalog.routes.ts (exposed by remote)
export const CATALOG_ROUTES: Routes = [
{ path: '', component: CatalogShellComponent, children: [
{ path: 'products', loadComponent: () => import('./product-list.component') },
]},
];
What are the main pros and cons of microfrontends?
Pros
- Independent deploys and faster squad velocity at scale
- Clear domain ownership and smaller codebases per team
- Incremental upgrades (Angular version per remote — with governance)
- Technology flexibility at integration boundaries
Cons
- Higher operational complexity (orchestration, shared deps, observability)
- Duplicated tooling and inconsistent UX without a strong platform team
- Performance overhead — multiple bundles, runtime loading
- Cross-cutting concerns (auth, analytics, i18n) need central design
- Testing integration is harder than a monolith
When should you NOT use microfrontends?
Avoid MFEs when:
- Small team / small app — a well-structured monolith with lazy routes is simpler.
- Tight UI coupling — features constantly share live state and components across domains.
- No DevOps maturity — independent deploys without CI contracts cause production breakage.
- Performance-critical single flow — checkout-like flows needing minimal JS may suffer from federation overhead.
- Organisation isn't aligned to domains — MFEs won't fix Conway's law; they'll amplify coordination cost.
Start monolith → modular monolith → extract MFEs when team scale and release pain justify it.
How do you test microfrontends effectively?
Use a testing pyramid per MFE plus integration layers:
- Unit tests — components, services, NgRx inside each remote (unchanged).
- Integration tests — remote boots with mocked host providers / shared libs.
- Contract tests — verify exposed federation modules export expected routes/components and semver.
- E2E (Cypress/Playwright) — run against composed environment (host + real remote URLs or stubs).
- Visual regression — per-MFE Storybook + optional full-shell snapshots for chrome consistency.
Stub remotes in host CI with module-federation dev-server proxies or mock remoteEntry.js for fast PR checks.
// Contract test idea (Jest)
it('exposes catalog routes', async () => {
const mod = await import('catalog/CatalogRoutes');
expect(mod.CATALOG_ROUTES).toBeDefined();
expect(mod.CATALOG_ROUTES[0].path).toBe('');
});
How would you apply microfrontends in a large enterprise portal (e.g. Netcracker-style BSS/OSS)?
Enterprise telecom/BSS portals are classic MFE candidates: many domains (catalog, quoting, inventory, billing), multiple squads, long-lived Angular codebases.
Recommended shape:
- Platform shell — SSO, tenant context, global nav, design system, audit logging.
- Domain remotes — Product Catalog, Order Capture, Service Activation as federated Angular apps.
- Shared libraries — auth SDK, API clients, UI kit published internally with semver.
- Governance — architecture guild defines routing prefixes, event naming (
nc:*), Angular upgrade windows. - Observability — correlate
traceId across MFE loads for support tickets.
Align MFE boundaries to business capabilities, not org chart politics.
Compare runtime integration patterns: Module Federation vs iframes vs build-time composition.
| Pattern | Deploy independence | Integration | Best for |
|---|
| Module Federation | High | Shared DOM/router; needs version discipline | Angular-only squads, SPA UX |
| iframes | Very high | Strong isolation; clunky routing, height, messaging | Legacy embeds, third-party widgets |
| Build-time (npm packages) | Low — host rebuild | Simplest DX, type-safe imports | Modular monolith stepping stone |
| Web Components | Medium–high | Framework-neutral custom elements | Mixed stacks, embeddable widgets |
Most Angular enterprise portals pick runtime federation for deploy independence with a single-router UX.
What performance considerations apply to Angular microfrontends?
MFEs add runtime loading cost — each remote is an extra network round-trip for remoteEntry.js and chunks.
- Lazy load on route — never fetch all remotes at bootstrap.
- CDN + HTTP caching — hash filenames; long cache for chunks, short for remoteEntry.
- Shared dependency deduplication — federation shared config prevents duplicate Angular.
- Prefetch — host prefetches likely next remote after idle (optional).
- Budgets per MFE — enforce max bundle size in CI.
- One Angular zone — multiple bootstrapped platforms hurt performance and UX.