SingleSPA delivers on its promise. We have React apps, some legacy Vue, even an Angular dashboard — all on the same page, lazy-loaded, independent deploy pipelines. The framework itself works, no question there.
It's the things around the framework that catch you.
To be fair, this isn't unique to SingleSPA. Module Federation teams face the same shared-dependency drift. Monorepos have their own version skew across packages. Even backend services wrestle with who owns the OpenAPI spec. Any distributed architecture eventually needs ownership, auditing, and dependency governance. SingleSPA just makes the gaps more visible because the import map becomes a central point of coordination — and nobody is watching it.
1. Nobody Owns the Import Map
The import map sits in the root config — a JSON file that determines which version of React every MFE loads at runtime. In theory, it's the single source of truth. In practice, nobody touches it.
The thing is, each team owns their MFE. Nobody owns the map. So nobody updates it. When a team retires an MFE, the entry stays. When a new shared dependency appears, nobody adds it. The map rots in plain sight, and the only person who notices is the one debugging a production incident at 2 AM.
We found one MFE still referenced in our import map that hadn't been deployed in eight months. Dead. Buried. Still loading in production. Nobody flagged it because nobody was looking.
I know what some of you are thinking — just auto-generate the import map from a build step, right? We tried. The problem is the map lives in a shared root config repo. Auto-generating it means giving every MFE's CI pipeline write access to that repo. Fourteen teams committing to the same JSON file on every deploy. You've traded ownership rot for merge conflicts. There are ways around it — we explored having a central build service that aggregates deployed MFEs and regenerates the map — but at 14 teams, the governance question is the same: who owns the service?
2. Compliance Drifts Without Anyone Noticing
The pattern is straightforward: mark React as an external in webpack or Vite config, let the import map serve it at runtime. Every MFE gets the same copy. No duplicate bundles.
Then someone refactors. They add a new feature, tweak the build config, and accidentally bundle React back in. The build passes. Tests pass. Deploy goes green. Nobody notices until:
- The bundle size report spikes
- A version mismatch breaks a shared component
- Someone opens Chrome DevTools and sees React loaded twice
We've had MFEs drift out of compliance for months. When we finally audited, two of our fourteen MFEs were shipping their own React — even though the import map said otherwise. The teams had no idea. Their builds were green. Why would they check?
The import map declares what should be shared at runtime. What the teams actually ship is a different story. The import map and the package.json declarations are two separate inputs, and they drift apart quietly.
Yes, you can catch some of this with CI checks — a build step that greps the webpack output for bundled React and fails the pipeline. But that only catches bundle duplication. It doesn't tell you that MFE A is on React 18.2 and MFE B is on 18.3, and the import map says 18.2. That's a version mismatch that won't fail a build but will break a shared component at runtime.
3. You Can't See What You Can't Measure
When someone on the platform team suggests bumping React from 18.2 to 18.3, the conversation goes like this:
"Which MFEs are on 18.2?"
Nobody knows.
"Can someone grep all the repos?"
Now you have fourteen browser tabs open, checking fourteen package.json files, while the meeting timer runs down. For a 14-team organisation to answer a version question with manual grepping — it's quite ridiculous, actually. But that's the default.
We eventually built a small tool. Feed it your import map URL and each MFE's package.json — pull them straight from the repo if you have access. It does two things:
Takes each MFE's
package.jsonand builds a version matrix. First run surfaced four different React versions across fourteen teams. Half the teams didn't even know.Cross-references the matrix against the import map. The map says what should be shared. The matrix shows what each MFE actually declares. The gap between them is your compliance drift. The map also catches things the
package.jsonfiles alone cannot — stale entries for decommissioned MFEs, broken CDN URLs, dependencies in the map that no MFE actually uses.
Under the hood, it pulls package.json from each repo's default branch via the GitHub API, normalises semver ranges to their resolved versions, and builds a dependency matrix across all MFEs. It then fetches the import map from your deployed root config URL and diffs the two. The output is a simple HTML report: green rows are compliant, red rows are conflicts or drifts, grey rows are stale entries no MFE references anymore. We wired it into a Monday morning cron job. Takes about 12 seconds for 14 repos.
Took an afternoon to build. We run it every Monday morning now. It's not a product — yet. It's just what we needed to stop firefighting.
4. The Tooling Gap is Real
import-map-overrides exists. It's useful for local development — toggle an MFE to load from localhost instead of the CDN. But it doesn't tell you that your import map has gone stale. It doesn't surface that payments-mfe is bundling React 17 while every other MFE is on 18. It doesn't alert you when a new dependency conflict appears.
We couldn't find an off-the-shelf dashboard that solved this for us. Some teams build Backstage plugins or wire compliance checks into CI. But those are bespoke — every large SingleSPA org we spoke to either built their own or just lived with the drift. There's no audit trail. No alert when the import map URL returns a 404. Platform teams are flying blind on what should be the simplest part of the stack.
Our biggest lesson wasn't technical. Every distributed architecture needs an owner for shared infrastructure. In SingleSPA, the import map is infrastructure. Treating it like a shared file instead of a managed asset is what got us into trouble.
I'm curious how other SingleSPA teams handle this. Do you run audits? Got a designated import map owner? Or is Monday morning just an alert storm and a lot of grepping? Genuinely want to know — we've got a thread on GitHub Discussions if you want to share how you're managing it. Because if we're alone in this, fine, we'll keep building. But I suspect we're not.
Top comments (1)
This is the real microfrontend problem: the framework can work while the coordination layer quietly decays. Import maps need ownership, review, version policy, and rollback paths. Otherwise the central map becomes a shared production dependency that nobody feels responsible for.