Micro Frontends Failed Because Web Components Failed

And we’re all pretending Webpack Module Federation is the answer

March 2, 2026

Look, I’m just gonna say it: we’re all out here building Micro Frontends with Module Federation and Single-SPA, writing 500-line webpack configs, debugging runtime errors at 2 AM... and nobody wants to admit that we’re just rebuilding what the browser was supposed to give us ten years ago.

Web Components were meant to solve this. They didn’t. And now we’re pretending JavaScript can fix what web standards couldn’t.

The Dream vs. Reality

Remember the promise? Write a component once, use it everywhere:

<checkout-widget user-id="123"></checkout-widget>\n<product-catalog category="electronics"></product-catalog>\n

Framework agnostic. No build step. Teams ship independently. The browser handles integration.

That was 2015. It’s 2025 now. And instead we’re doing this:

new ModuleFederationPlugin({\n  name: 'checkout',\n  exposes: { './Widget': './src/CheckoutWidget' },\n  shared: { \n    react: { singleton: true, requiredVersion: '^18.0.0' }\n  }\n})\n

We’re recreating browser features in webpack configs. This should feel weird.

Why Web Components Flopped

Shadow DOM killed it. Yeah, style isolation sounds great until you realize:

  • Your design tokens can’t penetrate it

  • Global theme switching becomes a nightmare

  • Every component needs its own styling logic

  • Third-party tools break

I’ve built Stencil components. I know the pain. You end up with this weird dance where you’re passing CSS variables through attributes, maintaining two styling systems, and explaining to designers why their global styles don’t “just work.”

React won because everything is just DOM. No magic boundaries. Your styles work the way you expect.

The DX was terrible. Compare these:

// Web Component\nclass MyWidget extends HTMLElement {\n  static get observedAttributes() {\n    return ['user-id', 'is-active'];\n  }\n  \n  attributeChangedCallback(name, oldValue, newValue) {\n    // Convert strings to types manually\n    // Track state yourself\n    // Re-render manually\n  }\n}\n\n// React\nfunction MyWidget({ userId, isActive }) {\n  return <div>Done</div>\n}

Even with Stencil or Lit helping, you’re fighting the platform. TypeScript support? Meh. HMR? Sometimes. DevTools? Good luck.

Nobody built libraries. Go find a production-ready Web Component date picker. I’ll wait.

The npm ecosystem went all-in on React/Vue/Angular. Web Components got some abandoned Polymer projects and internal design systems that died with their teams. Library authors optimize for reach, and React has 50% market share.

Enter Micro Frontends (The JavaScript Workaround)

So Web Components failed, but teams still needed autonomy. Cue Micro Frontends: “Framework agnostic! Independent deployment! Team autonomy!”

But look at what we actually built:

Module Federation is literally Shadow DOM in JavaScript. We’re managing isolation, shared dependencies, and version conflicts in webpack instead of letting the browser handle it. Same problem, worse tools, runtime overhead.

Single-SPA is CustomElementRegistry in userspace:

// What we wanted\ncustomElements.define('my-app', MyAppElement);\n\n// What we built\nregisterApplication('@company/my-app', () => import('./app'));

Same concept. More code. More bugs.

And the performance? Each micro frontend loads its own React, router, state management. You’re shipping 150kb+ per app because “team autonomy.” Users are paying for our org chart problems.

What Actually Works

After the Micro Frontend experiment fails (and it will), companies end up here:

apps/\n  shell/\n  checkout/  \n  products/\nshared/\n  design-system/

A monorepo. Build-time integration. Shared dependencies. One deployment. It’s not sexy, but it works.

The irony? Web Components actually succeeded in one place: design systems.

<company-button variant="primary">Click</company-button>

This works because design system components are stable, isolated, and truly framework-agnostic. Your React app can use the same button as your Angular app.

So here’s the thing: Your design system can be Web Components. Your apps should be monoliths.

The Uncomfortable Truth

Micro Frontends exist because we’re trying to solve organizational problems with architecture.

Teams can’t coordinate? Fix communication, don’t build distributed systems.

Need independent deploys? Use feature flags and proper CI/CD.

Want different frameworks? Ask yourself why, seriously.

If Web Components had worked, we’d have natural boundaries. We wouldn’t need webpack plugins to share React components. But they didn’t work, and now we’re building worse versions of browser features in JavaScript and calling it innovation.

Here’s My Take

Stop pretending Micro Frontends are the answer. They’re a workaround for a failed web standard.

If you’re doing it anyway:

  • Use build-time integration (Nx, Turborepo)

  • Deploy as one unit with feature flags

  • Actually consider if you need this complexity

If you’re using Web Components:

  • Design systems only

  • Skip Shadow DOM unless absolutely necessary

  • Stencil or Lit, never raw

The real solution? Well-architected monoliths with clear boundaries, good CI/CD, and teams that actually talk to each other.


Web Components were supposed to save us. They gave us Shadow DOM that isolated too much, DX that couldn’t compete, and an ecosystem that never materialized.

Micro Frontends are us rebuilding the same thing in JavaScript, shipping megabytes of overhead to users, and pretending it’s architecture.

Maybe the lesson is simple: standards move slow, frameworks move fast, and sometimes the worse technology wins because it solves today’s problems instead of tomorrow’s ideals.


Thanks for reading Under The Hood! Subscribe for free to receive new posts and support my work.

Leave a comment