@esmx/router is a framework-agnostic router built for modern micro-frontend applications. Unlike traditional routers that bind to a single framework, @esmx/router works with React, Vue 2, Vue 3, vanilla JavaScript, or any combination of them — all within the same application.
Modern web applications face challenges that traditional routers were not designed to solve:
@esmx/router was built from the ground up to address all of these.
The router does not import React, Vue, or any other framework. Instead, routes declare a micro-app — a set of callbacks (mount, unmount, renderToString) that know how to manage a specific framework's component tree. This means different routes can render using entirely different frameworks:
const router = new Router({
routes: [
{ path: '/', app: 'react-app', component: HomePage },
{ path: '/dashboard', app: 'vue3-app', component: Dashboard }
],
apps: {
'react-app': () => ({ mount(el, comp) { /* ReactDOM */ }, unmount(el) { /* cleanup */ } }),
'vue3-app': () => ({ mount(el, comp) { /* createApp */ }, unmount(el) { /* cleanup */ } })
}
});When the user navigates from / to /dashboard, the router unmounts the React app, mounts the Vue 3 app, and renders the correct component — all without a full page reload.
RouterMode.history: Uses the browser's History API (pushState, popstate) for standard web applicationsRouterMode.memory: Keeps state entirely in memory with no URL changes, for SSR, layer routing, and testingThe router has first-class SSR support. The same route configuration works on both server and client. On the server, use RouterMode.memory and pass req/res objects:
const router = new Router({
mode: RouterMode.memory,
base: new URL(req.url, `http://${req.headers.host}`),
req, res,
routes: [/* same routes as client */]
});
const html = await router.renderToString();Routes support dynamic parameters, nested children, lazy loading, per-route guards, redirects, micro-app binding, and more:
const routes = [
{
path: '/',
component: Layout,
children: [
{ path: '', component: Home },
{ path: 'users/:id', asyncComponent: () => import('./UserProfile') },
{ path: 'admin', app: 'admin-app', component: AdminPanel,
beforeEnter: (to, from, router) => { if (!isAdmin()) return '/login'; }
}
]
}
];Guards intercept navigation at every stage — from leaving the current route to entering the new one. The pipeline executes in this order:
fallback — Handle unmatched routesoverride — Route-level override (hybrid app scenarios)beforeLeave — Guard on the route being leftbeforeEach — Global guardbeforeUpdate — Guard when same route changes paramsbeforeEnter — Guard on the route being enteredasyncComponent — Lazy load the target componentconfirm — Final confirmation, DOM updates, micro-app mount/unmountafterEach — Post-navigation notificationGuards can return void (allow), false (cancel), a string/object (redirect), or a function (custom logic).
Layers are isolated routing contexts rendered on top of the main page — modals, drawers, and slide-in panels with their own navigation:
const result = await router.createLayer({
routes: [
{ path: '/', component: ModalContent },
{ path: '/step-2', component: ModalStep2 }
]
});
// result.data contains data passed to closeLayer()Inside a layer, navigation (push, replace, back) does not affect the parent page's route.
A framework-agnostic utility for building navigation links. router.resolveLink() returns attributes, active state, and event handlers that any framework can use:
const link = router.resolveLink({ to: '/about', activeClass: 'nav-active' });
// link.attributes — { href, class }
// link.isActive — true/false
// link.navigate — click handlerFramework-specific wrappers (like @esmx/router-vue) build their own <RouterLink> components on top of this.
The router automatically manages scroll positions:
push/replace — scrolls to top (unless keepScrollPosition: true)back/forward/go — restores the saved scroll positionhistory.stateFour error types provide structured error handling for navigation failures:
RouteTaskCancelledError — Navigation superseded by a newer oneRouteTaskExecutionError — A guard or async component threw an errorRouteNavigationAbortedError — A guard returned falseRouteSelfRedirectionError — Infinite redirect loop detected| Feature | @esmx/router | Vue Router | React Router |
|---|---|---|---|
| Framework-agnostic | ✅ | Vue only | React only |
| Multi-framework apps | ✅ | ❌ | ❌ |
| SSR support | ✅ Built-in | ✅ | ✅ |
| Navigation guards | ✅ Full pipeline | ✅ | Limited |
| Layer routing (modals) | ✅ Built-in | ❌ | ❌ |
| Micro-app lifecycle | ✅ | ❌ | ❌ |
| Memory mode | ✅ | ✅ | ✅ |
| Scroll management | ✅ Automatic | ✅ Manual | ❌ |
| TypeScript | ✅ Full types | ✅ | ✅ |