@llui/router
Router for LLui. Structured path matching with history and hash mode support.
pnpm add @llui/router
Usage
import { route, param, rest, createRouter, connectRouter } from '@llui/router'
import { div, a } from '@llui/dom'
// Define routes
const home = route([])
const search = route(['search'], (b) => b, ['q', 'page'])
const detail = route(['item', param('id')])
const docs = route(['docs', rest('path')])
// Create router
const router = createRouter({ home, search, detail, docs }, { mode: 'history' })
// Connect to effects system
const routing = connectRouter(router)
API
Route Definition
| Function | Description |
|---|---|
route(segments, builder?, queryKeys?) |
Define a route with path segments and optional query keys |
param(name) |
Named path parameter (e.g. /item/:id) |
rest(name) |
Rest parameter capturing remaining path |
Router
| Function | Description |
|---|---|
createRouter(routes, config) |
Create router instance (history or hash mode) |
connectRouter(router) |
Connect router to LLui effects, returns routing helpers |
Routing Helpers (from connectRouter)
| Method / Effect | Description |
|---|---|
.link(send, route, attrs, children) |
Render a navigation link with client-side routing |
.listener(send) |
Popstate listener -- call in view() to react to URL changes |
.handleEffect |
Effect handler plugin for navigate/push/replace effects |
.push(route) |
Push navigation effect |
.replace(route) |
Replace navigation effect |
.back() |
Navigate back effect |
.forward() |
Navigate forward effect |
.scroll() |
Scroll restoration effect |
Guards
Router guards let you block or redirect navigation. Pass beforeEnter and/or beforeLeave to connectRouter:
const routing = connectRouter(router, {
// Called before entering a new route
beforeEnter(to, from) {
// Return void -> allow
// Return false -> block
// Return Route -> redirect
},
// Called before leaving the current route
beforeLeave(from, to) {
// Return true -> allow
// Return false -> block
},
})
Guards run in the effect handler and the popstate listener, keeping update() pure.
Auth guard
const routing = connectRouter(router, {
beforeEnter(to) {
if (to.page === 'admin' && !isLoggedIn()) {
return { page: 'login' }
}
},
})
Unsaved changes guard
const routing = connectRouter(router, {
beforeLeave(from) {
if (from.page === 'editor' && hasUnsavedChanges()) {
return confirm('Discard unsaved changes?')
}
return true
},
})
Functions
createRouter()
function createRouter<R>(defs: RouteDef<any>[], config?: RouterConfig<R>): Router<R>
param()
Named path parameter: matches one segment
function param(name: string): ParamSegment
rest()
Rest parameter: matches remaining segments
function rest(name: string): RestSegment
route()
Define a route with structured path segments. @example route(['article', param('slug')], ({ slug }) => ({ page: 'article', slug })) route(['search'], { query: ['q'] }, ({ q }) => ({ page: 'search', q: q ?? '' }))
function route<R = any>(segments: Segment[], buildOrOpts: ((params: Record<string, string>) => R) | RouteDefOptions, buildOrToPath?: ((params: Record<string, string>) => R) | { toPath: (route: R) => string }): RouteDef<R>
Types
Segment
export type Segment = string | ParamSegment | RestSegment
Interfaces
RouteDef
export interface RouteDef<R> {
segments: Segment[]
build: (params: Record<string, string>) => R
queryKeys: string[]
/** Optional manual toPath override */
toPath?: (route: R) => string
}
Router
export interface Router<R> {
/** Match a pathname to a Route. Returns fallback if no match. */
match(pathname: string): R
/** Format a Route back to a pathname (base prefixed in history mode, no hash prefix). */
toPath(route: R): string
/** Format a Route to a full href (# prefix in hash mode, base prefix in history mode). */
href(route: R): string
/** The configured mode */
mode: 'hash' | 'history'
/** The normalized base path (empty string when none) */
base: string
/** All route definitions (for iteration) */
routes: ReadonlyArray<RouteDef<R>>
/** The fallback route */
fallback: R
}
RouterConfig
export interface RouterConfig<R> {
mode?: 'hash' | 'history'
fallback?: R
/**
* Base path (history mode only). All matched pathnames must start with it —
* a non-matching prefix resolves to `fallback`. `toPath`/`href` prepend it.
* Trailing slashes are normalized away, e.g. `'/app/'` → `'/app'`.
*/
base?: string
}