@gluonjs/router
The official Gluon router provides deterministic route matching, browser/hash/ memory histories, typed named routes, guards, failures, lazy route components, scroll restoration, and Gluon application bindings.
The package ships as part of the current 1.13.0 release line. Core and Reactivity are peers so an application has one shared application context and reactive identity.
@gluonjs/router at a glance
Runtime: browser · Release: 1.13.0
Documentation guide · npm · Source
Public API: @gluonjs/router · @gluonjs/router/memory
Install
npm install @gluonjs/routerQuick start
import { html } from '@gluonjs/core';
import { createRouter, createWebHistory } from '@gluonjs/router';
const router = createRouter({
history: createWebHistory('/app'),
routes: [{ path: '/', component: () => html`<main>Home</main>` }],
});Choose this package when
- Named routes, guards, lazy routes, and scroll restoration.
- Browser, hash, and memory history strategies.
- Application bindings that share the Gluon runtime context.
Choose another boundary when:
- Does not own component rendering or application state storage.
- History selection and guards live at the router boundary.
Related documentation
Stability notes
The router surface is stable in the current release line. Memory history is a supported DOM-free entry for Node, tests, and server resolution; unsupported transport, cache, and authentication policies remain outside the package.
Browser application
import { createApp, html } from '@gluonjs/core';
import {
RouterLink,
RouterView,
createRouter,
createRouterPlugin,
createWebHistory,
lazyRoute,
} from '@gluonjs/router';
const router = createRouter({
history: createWebHistory('/app'),
routes: [
{ path: '/', name: 'home', component: () => html`<h1>Home</h1>` },
{
path: '/reports/:id',
name: 'report',
component: lazyRoute(() => import('./report-page.js')),
},
],
scrollBehavior: (_to, _from, saved) => saved ?? { left: 0, top: 0 },
});
await router.isReady();
const app = createApp(() => html`
${RouterLink({ to: '/', children: 'Home' })}
${RouterView()}
`);
app.use(createRouterPlugin(router));
app.mount(document.querySelector('#app')!);RouterLink intercepts unmodified same-context clicks and exposes active and exact-active classes. RouterView({ depth, name }) selects nested and named route components. Unmounting the application destroys the installed router.
Typed named routes
type Routes = {
home: { params: {} };
report: { params: { id: string | number } };
};
const typedRouter = createRouter<Routes>({ history, routes });
await typedRouter.push({ name: 'report', params: { id: 42 } });Path params are encoded on generation and decoded on matching. Query keys are sorted during serialization; repeated values retain their input order. Prototype-like decoded keys remain frozen own data and never participate in the parser accumulator's prototype chain.
Route records may also provide static data. The router shallow-freezes and merges parent-to-child record data onto currentRoute.value.data; it never fetches, caches, or mutates this data. Keep request-dependent data in the application/store layer and use the normalized query for URL-owned filters, pagination, and shareable view state:
const router = createRouter({
history: createWebHistory('/app'),
routes: [{
path: '/catalog',
data: { title: 'Catalog', pageSize: 24 },
component: ({ route }) => html`
<h1>${route.data.title}</h1>
<p>Filter: ${route.query.filter ?? 'all'}</p>
`,
}],
});
await router.push({ path: '/catalog', query: { filter: 'featured' } });For request-backed route data, let the application or Store own the client, cache, pending/error/retry state, and revalidation. Pass its request-local AbortSignal through the async source and cancel it when the view owner is replaced. SSR and hydration use the request-local Router/Store snapshots; the Router itself remains request-free and has no authentication or transport policy.
Navigation control
beforeEach, record beforeEnter, and beforeResolve run in that order. Returning false aborts navigation; returning a location redirects it. push and replace resolve with an aborted, cancelled, or duplicatedNavigationFailure when applicable. Loader and hook errors reject navigation and are forwarded to onError handlers.
Lazy routes must use lazyRoute(() => import(...)). The explicit wrapper keeps ordinary functional components unambiguous and preserves production code splitting.
Memory and server use
Import @gluonjs/router/memory in Node, tests, or server rendering code. This entry point has no browser-history or Gluon UI binding export.
import { createMemoryHistory, createRouter } from '@gluonjs/router/memory';
const router = createRouter({
history: createMemoryHistory(['/reports/42?print=true']),
routes,
});
await router.isReady();
const snapshot = router.dehydrate();
await browserRouter.hydrate(snapshot);See the repository router contract for route syntax, history ownership, SSR handoff, failure, and scroll behavior.
License
MIT License, Copyright © 2026 Marc Malerei.