DEV Community

Taha Majlesi Pour
Taha Majlesi Pour

Posted on

🛠️ Refactoring Legacy React Apps: The Micro-Frontend Path

Our React app started like a puppy 🐶. Cute, small, easy.
Five years later, it was a Great Dane chewing through our furniture 🪑.

  • Every feature slowed us down.

  • Every release felt risky.

  • The “quick fixes” piled up into tech debt.

We needed a way to break it apart without hitting “rewrite from scratch.”
Micro-frontends became our path.

Here’s how we split a monolith React app into micro-frontends, the pitfalls we hit, and the lessons we’d pass on.


📖 A Quick Story

When we pitched micro-frontends to leadership, the promise sounded perfect ✨: independent teams, faster deployments, cleaner boundaries.

  • Reality check:

  • Routing chaos,

  • Dependency drift,

  • UI looking like a patchwork quilt 🎨.

  • And laptops screaming during local dev.

Still, breaking apart the monolith taught us valuable lessons worth sharing.


🗂️ Step 1: Define the Boundaries

You can’t just slice at random. We first mapped the app’s domains: auth, dashboard, checkout, settings. These became our candidate micro-frontends.

Tip: Slice by business capability , not by components. A “header micro-frontend” is a recipe for pain.


🛣️ Step 2: Routing the Split

The monolith’s react-router setup assumed one app. Micro-frontends demand a single “shell” that delegates routes.

Example:

<BrowserRouter>
  <Routes>
    <Route path="/dashboard/*" element={<DashboardApp />} />
    <Route path="/checkout/*" element={<CheckoutApp />} />
  </Routes>
</BrowserRouter>
Enter fullscreen mode Exit fullscreen mode

Pitfall : Overlapping routes caused chaos until we defined clear ownership of paths.


🔗 Step 3: Handling Shared State

Our monolith had one Redux store. Duplicating that across apps = trap.
We isolated state per domain, then created a shared session store for auth.

Example:

import { createStore } from 'zustand';

export const useSession = createStore((set) => ({
  user: null,
  setUser: (user) => set({ user })
}));
Enter fullscreen mode Exit fullscreen mode

Lesson: Share as little state as possible. Every extra slice ties teams back together.


⚙️ Step 4: Splitting the Build

We used Webpack Module Federation to load micro-frontends at runtime. Each team owned its build, but the shell wired them together.

Example:

new ModuleFederationPlugin({
  name: 'checkout',
  filename: 'remoteEntry.js',
  exposes: {
    './CheckoutApp': './src/App',
  },
});
Enter fullscreen mode Exit fullscreen mode

Pitfall: Version drift ⚠️. If one app shipped React 18 and another React 17 → runtime errors.


🎨 Step 5: Tackling UI Consistency

Once split, each team styled differently. The UI looked like a collage 🖼️.

Fix: We built a shared design system package. Components were pulled live, not copied.


💻 Step 6: Local Development Without Tears

At first, running the shell + every micro-frontend crushed laptops 🖥️🔥. Coffee cooled while builds spun.

Solution: Built mocks and stubs. Local dev usually meant one micro-frontend + stubbed neighbors.


🗺️ The Roadmap We’d Recommend

  • Map domains first — avoid atom-sized micro-frontends.

  • Centralize routing — clear ownership of URL paths.

  • Keep shared state minimal — only what must be global.

  • Align dependencies early — React versions, styling, libraries.

  • Adopt a design system early — stops UI drift.

  • Stub for local dev — save your laptop (and sanity).


📌 Practical Takeaways

  • Micro-frontends are surgery 🩺, not a silver bullet.

  • Split by business capability , not UI pieces.

  • Align on dependencies + design systems early.

  • Test local dev workflows before scaling to teams.


🎁 Something Extra (Resources)


🙌 More Like This? Let’s Connect!

📲 Follow me for more dev tips, tools, and trends!

🔑 Interface Insider (exclusive): Join the community – share, learn, and collaborate with other members!

Check out my latest dev articles and tutorials — updated weekly!

Let’s keep building cool stuff 🚀

Top comments (3)

Collapse
 
hashbyt profile image
Hashbyt

A very practical guide to taming a React monolith. Your emphasis on splitting by business capability, not components, is the crucial takeaway many teams miss.

Collapse
 
tahamjp profile image
Taha Majlesi Pour

Thanks! Yeah, that distinction made a huge difference for us — organizing by capability keeps things way saner over time.

Collapse
 
tahamjp profile image
Taha Majlesi Pour

🙌 Thanks for reading! Follow me for more front-end tips 💡