Parent
#114 — v0.41.0-alpha.5: SPA Mode + Deno Desktop Proof
What to build
Add a real client-side URL router to @openelement/router. Currently this package only has a data-context stack for loader/action hooks — no actual URL routing. This slice delivers the core routing engine: URL pattern matching, history-based and hash-based navigation, route params extraction, and async route guards.
Dual-mode navigation:
'history' mode (default) — uses pushState/popstate for clean URLs like /products/42
'hash' mode — uses #/products/42 for file:// and embedded contexts
'auto' mode — detects file:// protocol and switches to hash automatically
Route matching:
- Params extraction:
/products/:id → { id: '42' }
- Query string parsing:
?sort=asc&page=2
- Optional async guard functions that can block or redirect navigation
Integration with file-system routes:
- At build time, the Vite plugin generates a route manifest from
routes/*.tsx files
- Runtime consumes the manifest to match URLs to components
- Manual
defineRoute() also supported as escape hatch
Acceptance criteria
Blocked by
None — can start immediately
Parent
#114 — v0.41.0-alpha.5: SPA Mode + Deno Desktop Proof
What to build
Add a real client-side URL router to
@openelement/router. Currently this package only has a data-context stack for loader/action hooks — no actual URL routing. This slice delivers the core routing engine: URL pattern matching, history-based and hash-based navigation, route params extraction, and async route guards.Dual-mode navigation:
'history'mode (default) — usespushState/popstatefor clean URLs like/products/42'hash'mode — uses#/products/42forfile://and embedded contexts'auto'mode — detectsfile://protocol and switches to hash automaticallyRoute matching:
/products/:id→{ id: '42' }?sort=asc&page=2Integration with file-system routes:
routes/*.tsxfilesdefineRoute()also supported as escape hatchAcceptance criteria
createRouter({ mode: 'history' })returns a router that listens topushState/popstatecreateRouter({ mode: 'hash' })returns a router that uses#/segmentscreateRouter({ mode: 'auto' })picks hash whenlocation.protocol === 'file:'/products/:id/:slug?false) or redirect (return path string)navigate(path)andreplace(path)methodsrouter.dispose()removes all event listenersBlocked by
None — can start immediately