Getting Started
This guide walks you through setting up @esmx/router from scratch. By the end, you'll have a working router with routes, navigation, and framework integration.
Installation
Install the core router package:
If you're using Vue (2.7+ or 3), install the Vue integration as well:
npm install @esmx/router-vue
Basic Setup
At its simplest, @esmx/router needs a list of routes and a mode:
import { Router, RouterMode } from '@esmx/router';
const router = new Router({
appId: 'app',
mode: RouterMode.history,
routes: [
{ path: '/', component: HomePage },
{ path: '/about', component: AboutPage },
{ path: '/contact', component: ContactPage }
]
});
await router.push('/about');
console.log(router.route.path); // '/about'
console.log(router.route.query); // {}
That's it for the basics. The router matches URLs to components, handles browser history, and provides the current route state.
Setup with Vue 3
Vue 3 integration uses @esmx/router-vue which provides a plugin, composables, and components that work seamlessly with Vue's reactivity system.
1. Define Your Routes
src/routes.ts
import type { RouteConfig } from '@esmx/router';
export const routes: RouteConfig[] = [
{
path: '/',
component: Layout,
children: [
{ path: '', component: Home },
{ path: 'about', component: About },
{ path: 'users/:id', component: UserProfile }
]
}
];
2. Create the App
src/create-app.ts
import { h, createApp } from 'vue';
import { Router, RouterMode } from '@esmx/router';
import { RouterPlugin, useProvideRouter } from '@esmx/router-vue';
import App from './App.vue';
import { routes } from './routes';
export function createVueApp(router: Router) {
const app = createApp({
setup() {
useProvideRouter(router);
return () => h(App);
}
});
app.use(RouterPlugin);
return { app };
}
3. Client Entry
src/entry.client.ts
import { Router, RouterMode } from '@esmx/router';
import { createVueApp } from './create-app';
import { routes } from './routes';
const router = new Router({
appId: 'app',
mode: RouterMode.history,
routes
});
const { app } = createVueApp(router);
app.mount('#app');
4. Server Entry (SSR)
src/entry.server.ts
import type { RenderContext } from '@esmx/core';
import { Router, RouterMode } from '@esmx/router';
import { renderToString } from '@vue/server-renderer';
import { createVueApp } from './create-app';
import { routes } from './routes';
export default async (rc: RenderContext) => {
const router = new Router({
mode: RouterMode.memory,
base: new URL(rc.params.url, 'http://localhost'),
routes
});
await router.replace(rc.params.url);
const { app } = createVueApp(router, true);
const html = await renderToString(app, {
importMetaSet: rc.importMetaSet
});
rc.html = `<!DOCTYPE html>
<html>
<body>
<div id="app">${html}</div>
${rc.importmap()}
${rc.moduleEntry()}
</body>
</html>`;
};
5. Use in Components
src/App.vue
<template>
<div>
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
<RouterView />
</div>
</template>
<script setup lang="ts">
import { useRouter, useRoute } from '@esmx/router-vue';
const router = useRouter();
const route = useRoute();
function goToUser(id: string) {
router.push(`/users/${id}`);
}
</script>
Setup with Vue 2
Vue 2.7+ is supported using the same @esmx/router-vue package. The main difference is how the plugin is installed:
import Vue from 'vue';
import { Router, RouterMode } from '@esmx/router';
import { RouterPlugin, useProvideRouter } from '@esmx/router-vue';
import { routes } from './routes';
Vue.use(RouterPlugin);
const router = new Router({
appId: 'app',
mode: RouterMode.history,
routes
});
new Vue({
setup() {
useProvideRouter(router);
},
render: (h) => h(App)
}).$mount('#app');
In Vue 2.7+ components, you can use the same Composition API composables (useRouter, useRoute) just like in Vue 3. The Options API also works — the plugin makes this.$router and this.$route available:
export default {
mounted() {
console.log(this.$route.path);
this.$router.push('/about');
}
};
Setup with React
React doesn't have a dedicated integration package. Instead, register a micro-app directly on the router:
src/entry.client.tsx
import { Router, RouterMode } from '@esmx/router';
import { createRoot } from 'react-dom/client';
import { createElement } from 'react';
import { routes } from './routes';
const router = new Router({
appId: 'app',
mode: RouterMode.history,
routes,
apps: (router) => {
let root = null;
return {
mount(el) {
root = createRoot(el);
root.render(createElement(App, { router }));
},
unmount() {
root?.unmount();
root = null;
},
async renderToString() {
const { renderToString } = await import('react-dom/server');
return renderToString(createElement(App, { router }));
}
};
}
});
Then pass the router object through props or React context to your components:
function App({ router }: { router: Router }) {
const [route, setRoute] = useState(router.route);
useEffect(() => {
return router.afterEach((to) => setRoute(to));
}, [router]);
const Component = route.config?.component;
return Component ? <Component /> : null;
}
Project File Structure
A typical Esmx project with routing follows this structure:
src/
├── entry.node.ts # Node.js server setup, dev/build config
├── entry.server.ts # SSR rendering logic
├── entry.client.ts # Client-side mounting and app activation
├── create-app.ts # Shared app factory (used by both server & client)
├── routes.ts # Route definitions
├── App.vue # Root component
└── pages/
├── Home.vue
├── About.vue
└── UserProfile.vue
entry.node.ts: Configures the Node.js server (HTTP listener, middleware, build hooks)
entry.server.ts: Handles SSR — creates router in memory mode, renders HTML
entry.client.ts: Handles client-side — creates router in history mode, mounts and activates app
create-app.ts: Shared factory that creates the framework app with router
routes.ts: Single source of truth for route definitions
Full Working Example
Here's a complete example tying everything together with Vue 3 and SSR:
routes.ts
src/routes.ts
import type { RouteConfig } from '@esmx/router';
export const routes: RouteConfig[] = [
{
path: '/',
component: () => import('./layouts/MainLayout.vue'),
children: [
{ path: '', component: () => import('./pages/Home.vue') },
{ path: 'about', component: () => import('./pages/About.vue') },
{
path: 'users/:id',
asyncComponent: () => import('./pages/UserProfile.vue'),
meta: { requiresAuth: true }
}
]
}
];
create-app.ts
src/create-app.ts
import { h, createApp, createSSRApp } from 'vue';
import { Router } from '@esmx/router';
import { RouterPlugin, useProvideRouter } from '@esmx/router-vue';
import App from './App.vue';
export function createVueApp(router: Router, ssr = false) {
const create = ssr ? createSSRApp : createApp;
const app = create({
setup() {
useProvideRouter(router);
return () => h(App);
}
});
app.use(RouterPlugin);
return { app, router };
}
entry.client.ts
src/entry.client.ts
import { Router, RouterMode } from '@esmx/router';
import { createVueApp } from './create-app';
import { routes } from './routes';
const router = new Router({
appId: 'app',
mode: RouterMode.history,
routes
});
const { app } = createVueApp(router);
app.mount('#app');
entry.server.ts
src/entry.server.ts
import type { RenderContext } from '@esmx/core';
import { renderToString } from '@vue/server-renderer';
import { Router, RouterMode } from '@esmx/router';
import { createVueApp } from './create-app';
import { routes } from './routes';
export default async (rc: RenderContext) => {
const router = new Router({
mode: RouterMode.memory,
base: new URL(rc.params.url, 'http://localhost'),
routes
});
await router.replace(rc.params.url);
const { app } = createVueApp(router, true);
const html = await renderToString(app, {
importMetaSet: rc.importMetaSet
});
rc.html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
${rc.preload()}
${rc.css()}
</head>
<body>
<div id="app">${html}</div>
${rc.importmap()}
${rc.moduleEntry()}
${rc.modulePreload()}
</body>
</html>`;
};
entry.node.ts
src/entry.node.ts
import http from 'node:http';
import type { EsmxOptions } from '@esmx/core';
export default {
async devApp(esmx) {
return import('@esmx/rspack-vue').then((m) =>
m.createRspackVue3App(esmx)
);
},
async server(esmx) {
const server = http.createServer((req, res) => {
esmx.middleware(req, res, async () => {
const rc = await esmx.render({
params: { url: req.url }
});
res.end(rc.html);
});
});
server.listen(3000, () => {
console.log('Server started: http://localhost:3000');
});
}
} satisfies EsmxOptions;
What's Next?
Now that you have a working router, explore the rest of the guide: