In modern front-end systems, adopting a micro-frontend architecture allows independent teams to own discrete parts of a UI while still composing a seamless experience. One of the trickiest aspects in such systems is routing—how do you route between micro frontends, preserve state, and avoid coupling? In this article, we walk through a domain-based routing strategy (using subdomains or path prefixes) and how to implement it in a way that scales well and maintains separation of concerns.
Challenges & Design Goals
When building micro frontends, some of the core design goals are:
Isolation: Each micro frontend should be deployable independently and shouldn't require shared builds.
Lazy loading / code splitting: Users should load only what’s necessary.
Consistent navigation: The routing UX should feel seamless.
Shared state or cross-cutting concerns: e.g. global authentication, user profile, shared header/footer.
A common pattern is to use domain-based routing (or subdomain-based routing). For example:
app.example.com → the “shell” or host container
payments.example.com → payments micro frontend
dashboard.example.com → dashboard micro frontend
Alternatively you could use path prefixes (e.g. example.com/payments, example.com/dashboard), but domain routing helps in scoping cookies, caching, and SSL policies per domain.
Technical Implementation Outline
Here’s a skeleton of how you might implement this in a React + TypeScript environment with a Node.js backend:
DNS / Hosting Setup
Configure DNS + SSL so that *.example.com (or your domain wildcard) is routed to your hosting / load balancer layer. For instance, dashboard.example.com, payments.example.com, etc.
Edge / Gateway Layer
Use a gateway (e.g. NGINX, Traefik, or a serverless edge function) to route HTTP requests based on host header to the correct micro frontend service.
Shell / Host Application
The shell is the top-level container (or router) that might render common UI (e.g. header/navigation) and embed or forward users into micro frontends.
In React, you might dynamically import child apps. Example:
// in ShellApp.tsx
const App = () => {
const host = window.location.host; // e.g. "payments.example.com"
const Child = React.lazy(() => import(microfrontends/${host}));
return (
}>
);
};
Each micro frontend is built into a separately published bundle (e.g. via Webpack Module Federation or SystemJS).
Shared Context & Cross-App Communication
For shared concerns (auth, theme, user store), you can expose a small API on the shell, or use browser messaging / custom events to communicate between microfrontends. Another option is to share a minimal library bundle (ensuring version compatibility).
Handling Navigation & History Sync
When a user navigates within a micro frontend, you may want the shell to update a global breadcrumb or persist state across domain switches. You can use postMessage, BroadcastChannel, or even a shared cookie / localStorage channel to synchronize history or state.
Deployment & Versioning
Since each micro frontend is independently deployed, use versioned URLs (e.g. /v1/, /v2/) or hash-based bundles to avoid stale cache issues. Also coordinate API compatibility so that consumer and provider contracts don’t break.
Example Use Case: Music Dashboard + Analytics
Imagine you're building a music analytics platform with two micro frontends: a listener dashboard and an admin analytics panel. Suppose your brand is “Spotifull” (or you own a domain like spotifull.com). You might route:
dashboard.spotifull.com → the listener dashboard
admin.spotifull.com → analytics / management UI
The shell (running at www.spotifull.com) can mediate shared login, user context, and a unified top navigation. Each micro frontend then independently handles its own internal routing, state, and build pipeline. Because they live on different subdomains, caching headers, cookies, or even CDN configurations can be customized per sub-app.
Best Practices & Tips
Use Module Federation (Webpack 5) or a similar dynamic import mechanism to share or lazy-load microfrontends.
Limit the size of the shell — it should do only orchestration, not heavy UI logic.
Define contract interfaces (e.g. for events, APIs) in TypeScript so that microfrontends can evolve without surprise breaks.
Use health checks and circuit breakers in the gateway to gracefully degrade if one micro-app fails.
Monitor performance — cross-domain routing may introduce additional DNS/SSL overhead. Use HTTP/2 or HTTP/3 to reduce latency.




Imagine Spotify without limits no ads, no restrictions, just pure music. That’s exactly what Spotifull delivers. With our regularly updated Spotify Premium APK, you can stream and download songs seamlessly on any Android device, including the newest Android 15.
From offline downloads and endless skips to crystal-clear audio, Spotifull puts the full premium experience in your hands without draining your wallet.
👉 Start listening free: spotifull.com