Micro Frontends: The Magic of Decoupling
Breaking a monolithic frontend into independently deployable apps, how we wire them up with Module Federation, and how the pieces talk without stepping on each other.

Atharv Lingayat
Software Engineer

Micro frontends take the idea behind microservices and push it up into the browser. Instead of one giant frontend that every team commits to and everyone deploys together, you split the app into smaller pieces that ship on their own. Teams stop stepping on each other, releases get smaller, and the whole thing gets easier to reason about.
The clearest way to see it is the before and after. On the left, the monolith: one SPA, one build, one deploy, one place for everything to go wrong at once. On the right, the same product split by team.
From one monolith to many teams
Rather than a single app, each team owns a vertical slice of the product end to end. Cart, the marketing site, and payments each become their own app with their own service behind it, and they don't even have to agree on a framework. One team can stay on React while another runs Angular, and the user never notices the seam.
- Team Cart owns the cart experience and its cart service.
- Team Website owns the landing pages and their service.
- Team Payment owns checkout and payments, on whatever stack fits them.
How we're building it: Module Federation
I've been building this out with a teammate using Webpack Module Federation on top of Vite, React, and TypeScript. The host app doesn't bundle the other apps at build time. It pulls them in at runtime by loading their remoteEntry.js, so each micro frontend deploys on its own schedule and there's no code duplicated across builds.
// vite.config.ts (host)
import federation from "@originjs/vite-plugin-federation";
export default defineConfig({
plugins: [
federation({
name: "host",
remotes: {
cart: "https://cart.acme.dev/assets/remoteEntry.js",
},
shared: ["react", "react-dom"],
}),
],
});From there the host loads a remote like any other lazy component. If the cart team ships a new version, the host picks it up on the next load. No rebuild, no coordinated release.
import { lazy, Suspense } from "react";
// pulled from the cart micro frontend at runtime
const Cart = lazy(() => import("cart/Cart"));
export function CartRoute() {
return (
<Suspense fallback={<Spinner />}>
<Cart />
</Suspense>
);
}Why it's worth it
- Independent development and deployment. Each team ships its own feature without waiting on anyone else.
- Tech stack flexibility. Different apps can use different frameworks and upgrade on their own timeline.
- Faster iterations with less blast radius. A change in checkout can't take the whole app down with it.
Talking between the host and the MFEs with mitt
The tricky part of splitting things up is getting them to talk without gluing them back together. We use mitt as a tiny event bus. It's a few hundred bytes, and it lets the host and the micro frontends fire events at each other instead of importing each other's code. Something opens a modal or updates shared data, everyone who cares listens, and nobody is tightly coupled.
import mitt from "mitt";
export const bus = mitt();
// host fires an event
bus.emit("mfe:openModal", { id: "checkout" });
// a micro frontend reacts to it
bus.on("mfe:updateData", (payload) => {
syncCart(payload);
});Split by team, not by layer. When one team can build, ship, and break their slice without touching anyone else's, you've actually decoupled something.
The trade-offs worth knowing
It isn't free. You have to watch shared dependencies so two apps don't ship two different Reacts, keep the design language consistent when different teams own different screens, and accept a bit more setup up front. Reach for it when you actually have multiple teams and a frontend big enough to hurt. On a small app, a monolith is still the right call, and that's fine.
Micro frontends aren't magic, but the decoupling is close. Give teams a clean boundary and a lightweight way to talk across it, and the frontend starts to scale the way the backend already does.
Thanks for reading