How teams go from copy-paste chaos → internal npm packages → a monorepo that finally makes sense.
⚡ TL;DR: Micro frontends help teams move independently, but they also make code sharing harder than it looks. Internal npm packages can solve part of the problem, but they often add publishing and versioning overhead. A monorepo offers the same reuse with much less maintenance.
🧩 The Scale Problem with Micro Frontends
As frontend applications grow, teams break large apps into micro frontends — so different parts of the product can evolve independently.
In an ecommerce platform, that might look like:
| App | Responsibility |
|---|---|
| 🔐 Auth | Login, signup, session |
| 🛍️ Product Catalog | Listings, search, details |
| 🛒 Cart & Checkout | Cart state, payments |
| 👤 Account / Settings | Profile, preferences |
This works great for team autonomy — different teams own, ship, and evolve their pieces without blocking each other. 🚀
But as the number of MFEs grows, a problem quietly emerges:
⚠️ The hidden cost: Even though apps are split, they still rely on the same shared building blocks — formatters, API clients, hooks, types, business logic. Managing that shared code across repos is harder than it looks.
The shared building blocks typically include:
// Common code every MFE needs
price & currency formatters
API request helpers
shared product and user types
auth / session utilities
analytics tracking helpers
reusable hooks // useCart(), useAuth(), useDebounce()
business logic // discounts, tax, inventory checks
This is where things start getting messy. Teams solve it in one of two ways — and both have downsides.
📦 The Polyrepo + npm Package Approach
In a polyrepo setup, apps live in separate repositories:
auth-app/
product-catalog-app/
cart-checkout-app/
account-app/
To avoid duplication, shared code gets extracted into internal packages:
shared-ui/
shared-hooks/
shared-utils/
shared-types/
api-client/
✅ This is definitely better than copy-paste. Shared code is reusable and has clean boundaries.
The Release Friction Problem
But over time, a different kind of overhead creeps in. Even a small shared change now requires this entire workflow:
1. Update the shared package
↓
2. Raise a PR & get it reviewed
↓
3. Publish a new version to npm
↓
4. Bump dependencies in all consuming apps
↓
5. Retest each app independently
↓
6. Release everything separately
😓 That's a lot of process for changing one helper or one hook. As shared code grows, teams end up maintaining not just apps — but a whole ecosystem of internal packages. Code sharing starts feeling heavier than it should.
🏗️ What Is a Monorepo?
A monorepo is simply a setup where multiple apps and shared packages live inside the same repository.
apps/
auth-app/
product-catalog-app/
cart-checkout-app/
account-app/
packages/
shared-ui/
shared-hooks/
shared-utils/
shared-types/
api-client/
Apps consume shared code cleanly — just like a regular package, except there's no publish step:
import { Button, Modal } from '@repo/shared-ui'
import { useCart, useAuth } from '@repo/shared-hooks'
import { formatCurrency } from '@repo/shared-utils'
import { apiClient } from '@repo/api-client'
import type { Product } from '@repo/shared-types'
💡 Key insight: You no longer need to publish every shared abstraction as an npm package just to reuse it. That single change transforms the developer experience.
✅ How Monorepo Solved the Problem
Once teams move to a monorepo, shared code finally has a natural home. Instead of forcing every reusable piece into a separately published package, code is shared locally through internal workspaces.
The New Workflow
1. Update the shared code
↓
2. All apps use it immediately
↓
3. Test everything together
↓
4. Ship in one PR ✅
That gives teams the best of both worlds:
✨ Shared like packages. Managed like one system. Modular code organisation, clean boundaries, reuse across apps — without the publishing and versioning overhead.
Now when a shared utility, hook, or API client changed:
- All consuming apps could use it immediately
- Changes could be tested together
- Refactoring became easier
- Version drift disappeared
🎯 What Teams Gain
The biggest win isn't just cleaner code — it's less friction in day-to-day development.
🧹 Less Duplication
Shared code has a clear, single place to live. No more copy-paste divergence.
⚡ Faster Development
No more publish-bump-retest cycles for every small shared change.
🔧 Easier Refactoring
Shared code and its consumers can evolve together, atomically.
🎯 Better Consistency
All apps stay aligned on the same implementation, always.
📉 Lower Overhead
No more maintaining a growing ecosystem of tiny internal package repos.
🔍 Better Visibility
One place to search, one place to review, one place to understand the system.
☝️ One Important Lesson
Monorepo is powerful — but only when sharing is intentional, not automatic.
Only extract code into a shared package when it is:
- ✅ Used by two or more apps (not just one)
- ✅ Valuable to keep consistent across the system
- ✅ Stable enough to centralise without frequent churn
📌 If code is specific to one app, keep it local. Over-extraction creates a different kind of mess — a shared package that nobody quite owns. Intentional sharing is what keeps a monorepo healthy.
📊 Quick Comparison
| Approach | Pros | Cons |
|---|---|---|
| Copy-paste | Fast initially | Duplication, bugs multiply, inconsistency |
| Internal npm packages | Reusable, modular | Publishing overhead, version drift, slow iteration |
| Monorepo workspaces | Reusable + fast iteration + atomic refactors | Needs discipline, initial setup cost |
💭 Final Thoughts
"The best architecture isn't the one with the most packages — it's the one with the least friction."
Micro frontends solve real scaling problems. But as they grow, your code sharing strategy matters just as much as your app boundaries.
A monorepo ends up being the missing piece for most teams — it gives package-style modularity without the publishing overhead, and that makes a huge difference in everyday developer experience. 🚀

Top comments (1)
Nice post. I’ve seen MFE’s get super messy. IMO if you need them, should be: rare and small. Version drift and extensive workflows for a tiny change make it so not worth it in my eyes. No more abusing MFE’s!