# tiny-router **Repository Path**: mirrors_knownasilya/tiny-router ## Basic Information - **Project Name**: tiny-router - **Description**: Class based router with load hooks and SSR support - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-11-23 - **Last Updated**: 2026-02-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Tiny Router A tiny URL router for any app; * Zero dependencies. * It has good **TypeScript** support. * Framework agnostic. Can be used for **React**, **Preact**, **Vue**, **Svelte**, and vanilla JS. ```ts // router.ts import { Router, Page } from '@lifeart/tiny-router'; // Types for :params in route templates interface Routes { home: void category: 'categoryId' post: 'categoryId' | 'id' } export const router = new Router({ home: '/', category: '/posts/:categoryId', post: '/posts/:categoryId/:id' }) // async data loading per-route router.addResolver('post', async function(page) { const request = await fetch(`/api/pages/${page.params.id}`); const data = await request.json(); return data; }); // async component loading per-route router.addResolver('category', async function(page) { const MyCategory = await import('./components/my-category'); return { component: MyCategory.default } }); // add all routes handler router.addHandler((page: Page, data: any) => { console.log({page, data}); }); // router.activeRoute === null // change router state & resolve route await router.mount('/posts/42'); router.activeRoute.page.route === 'category'; router.activeRoute.page.path === '/posts/42'; ``` Store in active mode listen for `` clicks on `document.body` and Back button in browser. ```ts // components/layout.js import type { Page } from '@lifeart/tiny-router'; import { router } from './router.ts'; import { tracked } from '@glimmer/tracking'; import NotFoundComponent from './components/pages/not-found.hbs'; class RouteComponent extends Component { router = router.addHandler((page, data) = this.navigate(page, data)) @tracked page!: Page; @tracked routeComponent!: Component; navigate(page: Page, data: Component) { this.page = page; this.routeComponent = data.component || NotFoundComponent; } } ``` Stack based navigation: ```ts import { Router } from '@lifeart/tiny-router'; // lets define stack for route export const router = new Router({ "users": '/users', "users.user": '/users/:user', "users.user.card": '/users/:user/:card' }) ``` in this case, `users.user.card` route resolution will be started from `users`, then `users.user`, and at the end `users.user.card`; it mean we could "chain" resolvers: ```ts router.addResolver('users', () => { return [ { cards: [ { name: "hello" }] }]; }); router.addResolver('users.user', () => { const users = router.dataForRoute('users'); return users[0]; // { cards: [ { name: "hello" } ] } }); router.addResolver('users.user.card', () => { const user = router.dataForRoute('users.user'); return user.cards[0]; // { name: "hello" } }); ``` by default, resolved data cached by key params, way to invalidate it - ```ts router.unloadRouteData('users'); ``` in this case, this route will reload data Chained routing example: ```ts // components/layout.js import type { Page } from '@lifeart/tiny-router'; import { router } from './router.ts'; import { tracked } from '@glimmer/tracking'; import NotFoundComponent from './components/pages/not-found.hbs'; import Component, { hbs } from '@glimmerx/component'; interface IStackedRouter { stack: { name: string, data: null | unknown }[]; components?: Record } const DefaultRoute = hbs` {{#if @hasChildren}} {{yield}} {{/if}} `; class StackedRouter extends Component { get tail() { return this.parts.tail; } get parts() { const [ head, ...tail] = this.args.stack; return { head, tail } } get components() { return this.args.components ?? {}; } get Component() { return this.model?.component || this.components[this.route] || DefaultRoute; } get route() { return this.parts.head.name; } get model() { return (this.parts.head.data || {}) as Record; } static template = hbs` {{#if @stack.length}} {{/if}} ` } class RouteComponent extends Component { router = router.addHandler((page, data, stack) = this.navigate(page, data, stack)); @tracked stack = []; @tracked params = {}; navigate(page: Page, _ : any, stack: [string, any][]) { this.params = page.params; this.stack = stack; } static template = hbs` ` } ``` ## Install ```sh yarn add @lifeart/tiny-router ``` ## Usage See [Tiny Router docs](https://github.com/lifeart/tiny-router#guide) about using the router and subscribing to changes in UI frameworks. ### Routes For string patterns you can use `:name` for variable parts. For TypeScript, you need specify interface with variable names, used in routes. ```ts interface Routes { routeName: 'var1' | 'var2' } new Router({ routeName: '/path/:var1/and/:var2' }) ``` ### URL Generation Using `getPagePath()` avoids hard coding URL in templates. It is better to use the router as a single place of truth. ```tsx import { getPagePath } from '@lifeart/tiny-router' … ``` If you need to change URL programmatically you can use `openPage` or `redirectPage`: ```ts import { openPage, redirectPage } from '@lifeart/tiny-router' function requireLogin () { openPage(router, 'login') } function onLoginSuccess() { // Replace login route, so we don’t face it on back navigation redirectPage(router, 'home') } ``` ### Server-Side Rendering Router can be used in Node environment without `window` and `location`. In this case, it will always return route to `/` path. You can manually set any other route: ```js if (isServer) { router.open('/posts/demo/1') } ```