About
The router module turns multi-page websites into fluid PJAX applications while keeping server-rendered HTML as the source of truth.
It is inspired by Barba.js, but implemented and adapted for Riba.js modules, binders, and component lifecycle.
Why use @ribajs/router?
- Smooth page transitions without full page reload.
- Lower network overhead with cache and prefetch.
- Works with CMS outputs (WordPress, OctoberCMS, Shopify, static builds).
- Progressive enhancement friendly: pages still work without JavaScript.
- Keeps your app architecture close to regular multi-page websites.
How it works
When the user clicks an eligible link:
- The click is intercepted (either globally or via
rv-route). - URL state is updated with the History API.
- The next page HTML is fetched (or loaded from cache).
- The target container is parsed and appended into
router-view. - A transition is resolved and executed.
- The old container is removed and the new one stays visible.
- Router events and hooks are emitted for app code.
For a complete event and lifecycle breakdown see Router Lifecycle and
Router Hooks pages.
Credits
The transition API and documentation structure are inspired by Barba.js (MIT License) by Luigi De Rosa and Thierry Michel.
Install
npm install --save @ribajs/router
Register module
Register the router module next to your core module:
import { Riba, coreModule } from "@ribajs/core";
import { routerModule } from "@ribajs/router";
const riba = new Riba();
const model = {};
riba.module.register(coreModule.init());
riba.module.register(routerModule.init());
riba.bind(document.body, model);
Configure transitions (optional)
You can keep the default transition or provide declarative transitions:
riba.module.register(
routerModule.init({
transitions: [
{
name: "fade",
leave: ({ current }) => {
current.container?.animate(
[{ opacity: 1 }, { opacity: 0 }],
{ duration: 200, fill: "forwards" },
).finished;
},
enter: ({ next }) => {
next.container?.animate(
[{ opacity: 0 }, { opacity: 1 }],
{ duration: 200, fill: "forwards" },
).finished;
},
},
],
}),
);
Required markup
<router-view id="main">
<main data-namespace="home">
<!-- content swapped by router -->
</main>
</router-view>
Use unique data-namespace values per page whenever possible. They are used for
transition rules and hooks.
Markup and Basic Transition
The minimum structure is a router-view wrapper with a page container child.
<router-view>
<main data-namespace="home">
<h1>Home</h1>
</main>
</router-view>
router-view keeps layout-level elements stable while replacing its content
container between navigations.
Basic transition example
import { routerModule } from "@ribajs/router";
routerModule.init({
transitions: [
{
name: "basic-fade",
leave: ({ current }) =>
current.container?.animate(
[{ opacity: 1 }, { opacity: 0 }],
{ duration: 220, fill: "forwards" },
).finished,
enter: ({ next }) =>
next.container?.animate(
[{ opacity: 0 }, { opacity: 1 }],
{ duration: 220, fill: "forwards" },
).finished,
},
],
});
For rule-based transitions (from / to, sync, once) continue with
the Router Transitions page.
Events
During all the lifecycle of the page transition, the router module will emit a series of events, with useful information:
| Name | Arguments | Description |
|---|---|---|
| linkClicked | HTMLElement, MouseEvent |
The user has clicked on a link eligible for PJAX. |
| initStateChange | currentStatus |
The link has just been changed. |
| newPageReady | viewId, currentStatus, prevStatus, HTMLElementContainer, newPageRawHTML, containerDataset isFirstPageLoad |
The new container has been loaded and was injected |
| transitionCompleted | viewId, currentStatus [, prevStatus] |
The transition has just finished and the old Container has been removed from the DOM. |
currentStatus and prevStatus are plain objects with the URL of the page and optional namespace:
export interface State {
url: string;
namespace?: string | null;
}
To listen for an event, it's as simple as:
import { EventDispatcher } from "@ribajs/events";
import type { State } from "@ribajs/history";
const dispatcher = new EventDispatcher("main");
dispatcher.on(
"newPageReady",
(
viewId: string,
currentStatus: State,
prevStatus: State,
container: HTMLElement,
newPageRawHTML: string,
dataset: any,
isFirstPageLoad: boolean,
) => {
// your listener
},
);
You can also observe transition lifecycle through routerHooks:
import { routerHooks } from "@ribajs/router";
routerHooks.before((data) => {
console.info("Starting transition", data.current.url.href, "->", data.next.url.href);
});
Next Steps
Continue withLifecycle,Transitions,Hooks,BindersandComponentsdocumentation.