airx-router
v0.7.2
Published
Front-end routing for airx
Readme
router

Front-end routing for airx
Install
npm install airx-routerRequires airx as a peer dependency:
npm install airxUse
import * as airx from 'airx'
import { RouteComponentProps, Router } from 'airx-router'
function TestRouteComponent(props: RouteComponentProps) {
// Matching child routes will be rendered here
return () => props.children
}
const route = {
path: '/root',
component: TestRouteComponent,
children: [
{
path: '/',
redirect: 'child-1'
},
{
path: 'child-1',
component: TestRouteComponent,
children: [
{
path: '/',
redirect: 'child-2'
},
{
path: 'child-2',
component: TestRouteComponent
}
]
}
]
}
airx
.createApp(<Router routes={[route]} />)
.mount(document.getElementById('app'))API
<Router>
The main router component that manages routing state and renders matched routes.
Props
routes: Route[] | Route- Route configuration, can be a single route or arrayhistory?: History- Optional history instance (defaults tocreateBrowserHistory())
Returns
A render function that returns the current matched route element.
Route Types
// Path route - renders a component
interface PathRoute {
path: string
name?: string
meta?: Record<string, unknown>
component: AirxComponent<RouteComponentProps>
children?: Route[]
}
// Redirect route - redirects to another path
interface RedirectRoute {
path: string
name?: string
meta?: Record<string, unknown>
redirect: string
}RouteComponentProps
interface RouteComponentProps {
data: MatchResult // Path matching results with params
children: AirxElement<RouteComponentProps>[] // Nested child route elements
}useRouter()
Hook to access the history instance from anywhere in the component tree:
import { useRouter } from 'airx-router'
function MyComponent() {
const history = useRouter()
const handleClick = () => {
history.push('/new-path')
}
return <button onClick={handleClick}>Navigate</button>
}Path Matching
Static Paths
{ path: '/page', component: PageComponent }Matches exactly /page.
Root Path
{ path: '/', component: HomeComponent }Matches the root path /.
Dynamic Segments
{ path: '/user/:id', component: UserComponent }
// Matches: /user/1, /user/abc, etc.
// data.params.id will contain the matched valueOptional Segments
{ path: '/page/:tab?', component: PageComponent }
// Matches: /page, /page/settings, etc.Catch-all
{ path: '/:**', component: NotFoundComponent }
// Matches any unmatched pathNested Paths
Child paths are matched relative to their parent:
{
path: '/parent',
component: ParentComponent,
children: [
{ path: 'child', component: ChildComponent } // Matches /parent/child
]
}Redirects
Absolute Redirect
{ path: '/old', redirect: '/new' }Relative Redirect
{
path: '/parent',
component: ParentComponent,
children: [
{ path: '/', redirect: 'child' } // Redirects to /parent/child
]
}Path Traversal
{ path: '/a/b', redirect: '../c' } // Redirects to /a/cHistory Modes
Browser History (default)
Uses HTML5 History API:
<Router routes={routes} />Memory History
Useful for testing or server-side rendering:
import { createMemoryHistory } from 'history'
const history = createMemoryHistory({ initialEntries: ['/'] })
<Router routes={routes} history={history} />Hash History
For environments without server configuration:
// Use hash-based paths
const history = createMemoryHistory({ initialEntries: ['#/page'] })
<Router routes={routes} history={history} />Route Meta
Routes can include metadata for guards, permissions, etc:
{
path: '/admin',
component: AdminComponent,
meta: {
requiresAuth: true,
roles: ['admin'],
title: 'Admin Panel'
}
}Type Guards
import { isRedirectRoute, isPathRoute } from 'airx-router'
if (isRedirectRoute(route)) {
// route.redirect is available
} else if (isPathRoute(route)) {
// route.component and route.children are available
}Limitations
Context Requirement
The Router must be used within an airx component context. It uses provide/inject for history distribution.
Single Router Instance
Currently, only one Router instance per app is supported.
No Built-in Guards
This library focuses on routing; authentication/authorization guards should be implemented at the component level or via a wrapper.
Path Format
- Paths must start with
/for root-relative matching - Child paths should not start with
/(they're relative to parent) - Empty path
''is treated as equivalent to/
Signal Dependency
Requires signal-polyfill or native Signal support in the runtime environment.
Testing
Run tests with:
npm test # Watch mode
npm run test:run # Single run
npm run test:ui # UI modeLicense
MIT
