Adding Routing
The router tracks the URL and matches it against your route definitions.
The Simplest Router
Section titled “The Simplest Router”Start with just route definitions and a router:
import { compose } from '@rimitive/core';import { SignalModule, ComputedModule } from '@rimitive/signals/extend';import { createRouterModule } from '@rimitive/router';
// Define routes - pure dataconst routes = [ { id: 'home', path: '' }, { id: 'about', path: 'about' },];
// Compose with routerconst svc = compose( SignalModule, ComputedModule, // Add this 👇 createRouterModule(routes));
const { router } = svc;The router gives you reactive signals:
router.currentPath(); // '/' or '/about'router.matches(); // [{ id: 'home', pattern: '/', params: {}, path: '/' }]Navigate programmatically:
router.navigate('/about');router.currentPath(); // '/about'router.matches(); // [{ id: 'about', pattern: '/about', params: {}, path: '/about' }]Rendering Routes
Section titled “Rendering Routes”Use match() to render based on the matched route:
// Page componentsconst Home = ({ el }: Service) => () => el('div')( el('h1')('Home'), el('p')('Welcome!'));
const About = ({ el }: Service) => () => el('div')( el('h1')('About'), el('p')('Learn more about us.'));
const NotFound = ({ el }: Service) => () => el('div')( el('h1')('404'), el('p')('Page not found.'));
// App with routingconst App = (svc: Service) => () => { const { el, match, router } = svc; const HomePage = svc(Home); const AboutPage = svc(About); const NotFoundPage = svc(NotFound);
const pages: Record<string, () => ReturnType<typeof el>> = { home: HomePage, about: AboutPage, };
return el('div')( match(router.matches, (matches) => { const route = matches[0]; if (!route) return NotFoundPage();
const Page = pages[route.id]; return Page ? Page() : NotFoundPage(); }) );};
const AppComponent = svc(App);const app = mount(AppComponent());document.body.appendChild(app.element!);When router.matches changes, match() swaps the rendered component.
Adding Navigation
Section titled “Adding Navigation”Use Link for declarative navigation that works with the router:
import { createLinkModule } from '@rimitive/router/link';
// Add to service compositionconst svc = compose( // ... other modules createLinkModule());
const Nav = ({ el, Link }: Service) => () => el('nav')( Link({ href: '/' })('Home'), Link({ href: '/about' })('About'));
const App = (svc: Service) => () => { const { el, match, router } = svc; const NavComponent = svc(Nav); const NotFoundPage = svc(NotFound);
// ... page setup from earlier
return el('div')( NavComponent(), el('main')( match(router.matches, (matches) => { const route = matches[0]; if (!route) return NotFoundPage(); const Page = pages[route.id]; return Page ? Page() : NotFoundPage(); }) ) );};Link renders an <a> tag that intercepts clicks and uses router.navigate().
For programmatic navigation:
const Home = ({ el, router }: Service) => () => el('div')( el('h1')('Home'), el('button').props({ onclick: () => router.navigate('/about') })('Go to About'));Route Parameters
Section titled “Route Parameters”Capture dynamic segments with :param syntax:
const routes = [ { id: 'home', path: '' }, { id: 'products', path: 'products' }, { id: 'product-detail', path: 'products/:id' },];When the URL is /products/123, the match includes the parameter:
router.navigate('/products/123');router.matches();// [{ id: 'product-detail', pattern: '/products/:id', params: { id: '123' }, path: '/products/123' }]Use the params in your component:
const ProductDetail = ({ el }: Service) => (params: { id: string }) => el('div')( el('h1')(`Product ${params.id}`), el('p')('Product details here...'));
// In the App component's router matchconst ProductDetailPage = svc(ProductDetail);
match(router.matches, (matches) => { const route = matches[0]; if (!route) return NotFoundPage();
if (route.id === 'product-detail') { return ProductDetailPage(route.params as { id: string }); }
const Page = pages[route.id]; return Page ? Page() : NotFoundPage();});Query Strings
Section titled “Query Strings”The router parses query strings into reactive signals:
// URL: /products?sort=price&category=electronics
router.search(); // '?sort=price&category=electronics'router.query(); // { sort: 'price', category: 'electronics' }React to query changes:
const Products = ({ el, computed, router }: Service) => () => { const sortOrder = computed(() => router.query().sort || 'name');
return el('div')( el('h1')('Products'), el('p')(computed(() => `Sorted by: ${sortOrder()}`)), el('button').props({ onclick: () => router.navigate('/products?sort=price') })('Sort by Price') );};Active Link Styling
Section titled “Active Link Styling”Use router.currentPath to style the active link:
const NavLink = ({ computed, router, Link }: Service) => (href: string, label: string) => { const isActive = computed(() => router.currentPath() === href);
return Link({ href, className: computed(() => isActive() ? 'nav-link active' : 'nav-link') })(label);};
const Nav = (svc: Service) => () => { const { el } = svc; const navLink = svc(NavLink);
return el('nav')( navLink('/', 'Home'), navLink('/about', 'About'), navLink('/products', 'Products') );};