Mastering Micro‑Frontend Routing: From Basics to Sandbox Isolation
This article explores the challenges of routing in micro‑frontend architectures, explains why correct parent‑child routing is essential, walks through progressively more sophisticated solutions—including shared routes, sandbox isolation, and message‑based synchronization—and finally presents a robust implementation that works across diverse scenarios.
Problem Background and Desired Effect
Even though routing occupies only a small part of the micro‑frontend core, it touches integration logic, sandboxing, and other critical mechanisms. Solving routing correctly is non‑trivial, and examining it offers insight into the whole micro‑frontend system.
Typical Scenarios
Large‑scale modular development (giant‑stone applications).
Co‑existence of old and new projects.
Third‑party system integration.
Depending on the scenario, implementation details differ. For example, third‑party integration must support multiple tech stacks, while internal Alibaba projects mainly care about React version compatibility. Some applications (e.g., multi‑tab mail) need isolation per tab, whereas single‑page exclusive apps do not.
Desired Parent‑Child Routing Effect
The goal is that after integration the main app’s URL reflects the active child app and navigation works as expected:
Visiting cow-and-chicken.com/cow/detail loads the cow app’s detail page.
When the child app changes its internal route, the main app’s URL updates accordingly (e.g., cow-and-chicken.com/cow/ after returning home).
Browser forward/back works normally.
The solution should be universal, require no modifications to child apps, and avoid special conventions.
Position of Routing in the Micro‑Frontend Stack
Routing is intertwined with loading, rendering, communication, sandboxing, and JS bundle execution. It also relates to tooling that adapts child apps and configuration management that supplies child‑app metadata.
Full‑Stack Overview
The core micro‑frontend kernel (pure‑technology) is reusable across business scenarios, while platform‑level features (configuration, control) are customized per BU.
Page State and Front‑End Routing
Why do we need front‑end routing? Historically, MVC introduced a controller layer, but the controller became a glue‑code nightmare. Routers (e.g., Backbone) replaced controllers, providing a clean mapping from URL to view state.
// maintain current page state
var currentState = 'default';
// route table
var stateMap = {
'default': () => {},
'cow/home': () => {},
'chicken/home': () => {}
};
controller.on('statechange', e => {
stateMap[e.type](e.payload);
});
document.on('click', e => {
switch (e.target.className) {
case 'cow-home-button':
currentState = 'cow/home';
break;
}
controller.emit('statechange', {type: currentState, payload: {}});
});History API
The History API supersedes hash‑based routing, allowing URL changes without page reloads. However, pushState does not affect the URL if the developer chooses not to expose state, which is rarely desired.
// pushState without changing URL
history.pushState('new state', '', undefined);Gradual Implementation of a Perfect Parent‑Child Routing Logic
Parent Does Not Handle Routing
The parent merely loads and renders children; children manage their own routes, and the URL reflects the child directly.
Shared Routing Between Parent and Children
The parent dispatches children based on the URL, and children prepend a unique prefix (e.g., their name) to avoid conflicts.
// prefix routes with app name
const APP_NAME = 'cow';
<Route path={`/${APP_NAME}/home`} component={Home} />
<Route path={`/${APP_NAME}/detail`} component={Detail} />Sandbox Isolation
To avoid global side‑effects, each child runs inside a sandbox (iframe). Global objects like window, document, location, and history are proxied or replaced, and a whitelist can be used for intentional sharing.
// wrap child app with a sandbox
__CONSOLE_OS_GLOBAL_HOOK(id, function(exports, require, module, {window}) {
// child code
});
const frame = document.createElement('iframe');
const _window = frame.contentWindow;
function __CONSOLE_OS_GLOBAL_HOOK(id, entry) {
entry(exports, require, module, {window: _window});
}Proxying history enables interception of pushState to forward messages to the parent.
class History {
constructor(_history) {
return new Proxy(_history, {
get(target, name) {
switch(name) {
case 'pushState':
return (...args) => {
const returnValue = _pushState.call(_history, ...args);
frame.postMessage({type: 'statechange', data: {location: frame.location, state: _history.state}}, '*');
return returnValue;
};
}
}
});
}
}Synchronizing Child Route Back to Parent
The parent listens for postMessage events from the sandbox and updates its own URL using replaceState, avoiding a popstate loop.
frame.contentWindow.addEventListener('message', e => {
const payload = e.data;
if (payload.type === 'statechange') {
const {state, title, location} = payload.data;
const url = location.href;
window.history.replaceState(state, title, url);
}
});Joint Session History
When an iframe navigates, its history is merged with the top‑level page’s history (Joint Session History). This means history.length grows with each child navigation, and parent back/forward controls affect the iframe.
Using replaceState on the parent after receiving a child route change yields the desired effect without extra entries.
Conclusion
The article walked through the role of routing in micro‑frontend systems, why routing synchronization matters, and how to build a robust solution step by step—from naïve parent‑only handling, to shared prefixes, to sandbox isolation, and finally to message‑driven synchronization that respects Joint Session History.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
