Minimal, framework-agnostic client-side router for vanilla TypeScript apps.
npm install @phcdevworks/spectre-shell-routerA minimal, framework-agnostic client-side router for vanilla TypeScript apps. Designed to support the Spectre platform's app shell with simple URL-to-page mapping and lifecycle management.
π€ Contributing Guide | π Changelog
@phcdevworks/spectre-shell-router is not a full routing framework. It intentionally solves one problem: mapping URLs to page modules and managing page lifecycle in a vanilla web app.
- β
Maps URL paths to async page module loaders
- β
Supports path parameters (e.g., /users/:id)
- β
Uses the browser History API (pushState, popstate)
- β
Loads pages dynamically via import()
- β
Calls page render(ctx) and optional destroy() lifecycle hooks
- β
Minimal surface areaβeasy to delete or replace
``bash`
npm install @phcdevworks/spectre-shell-router
`typescript
import { Router } from '@phcdevworks/spectre-shell-router'
const routes = [
{ path: '/', loader: () => import('./pages/home') },
{ path: '/users/:id', loader: () => import('./pages/user') },
{ path: '/about', loader: () => import('./pages/about') },
]
const router = new Router(routes, document.getElementById('app'))
`
Each page module must export a render function:
`typescript
// pages/user.ts
export function render(ctx: RouteContext) {
const userId = ctx.params.id
ctx.root.innerHTML =
}export function destroy() {
// Optional cleanup
console.log('Leaving user page')
}
`$3
`typescript
// Programmatic navigation
router.navigate('/users/123')// Or use standard links with History API interception
About
`Page Contract
Each page module must export:
`typescript
export function render(ctx: RouteContext): void
export function destroy?(): void // optional
`Where
RouteContext contains:-
path β the matched URL path
- params β route parameters (e.g., { id: '123' })
- query β URLSearchParams object
- root β the DOM element where the page should renderWhat It Does
β
Maps URL paths to page loaders
β
Path parameter extraction (
/users/:id)
β
History API integration
β
Dynamic page imports
β
Lifecycle hooks (render, destroy)What It Does NOT Do
β No nested routing
β No layouts or middleware
β No guards or data fetching
β No global state management
β No SSR or hydration
β No transitions or animations
β No framework lifecycle integration
Development Philosophy
This router follows a minimal by design approach:
$3
Purpose: Map URLs to pages and manage lifecycle
Rules:
- Keep the API surface minimal and predictable
- No framework dependencies or assumptions
- Router only maps paths and manages lifecycle
- TypeScript strict mode with proper type exports
$3
Purpose: Simple interface for pages to integrate
Contains:
-
render(ctx) - Required page entry point
- destroy() - Optional cleanup handler
- RouteContext - Type-safe context objectRules:
- Pages handle their own rendering
- No magic or hidden behavior
- Clear separation of concerns
$3
Routing should be boring, small, explicit, and replaceable.
This router exists to define a navigation and page lifecycle contract, not to compete with full-featured frameworks.
Design Principles
1. Minimal surface area - Only essential routing features
2. Framework-agnostic - Works with any rendering approach
3. Type-safe - Full TypeScript support with proper exports
4. Disposable by design - Easy to replace if a framework is adopted later
5. Production-ready - Simple, tested, and reliable
TypeScript Support
Full TypeScript definitions are included:
`typescript
import type { Router, RouteContext, Route } from '@phcdevworks/spectre-shell-router'
``- Spectre Tokens - Design token foundation
- Spectre UI - Core styling layer
- Spectre Shell Router - Client-side routing (this package)
- Spectre Shell - Application shell framework
- Spectre Blocks - WordPress block library
- Spectre Astro - Astro integration
Issues and pull requests are welcome. For detailed contribution guidelines, see CONTRIBUTING.md.
MIT Β© PHCDevworks β See LICENSE for details.
---
If Spectre Shell Router helps your workflow, consider sponsoring: