A tiny URL router for Nano Stores state manager.
- Small. 808 bytes (minified and gzipped). Zero dependencies. It uses Size Limit to control size.
- It has good TypeScript support.
- Framework agnostic. Can be used for React, Preact, Vue, Svelte, and vanilla JS.
Since Nano Stores promote moving logic to store, the router is a store, not a component in UI framework like React.
// stores/router.ts
import { createRouter } from '@nanostores/router'
// Types for :params in route templates
interface Routes {
home: void
category: 'categoryId'
post: 'categoryId' | 'id'
}
export const router = createRouter<Routes>({
home: '/',
category: '/posts/:categoryId',
post: '/posts/:categoryId/:id'
})
Store in active mode listen for <a>
clicks on document.body
and Back button
in browser.
// components/layout.tsx
import { useStore } from '@nanostores/react'
import { router } from '../stores/router.js'
export const Layout = () => {
const page = useStore(router)
if (!page) {
return <Error404 />
} else if (page.route === 'home') {
return <HomePage />
} else if (page.route === 'category') {
return <CategoryPage categoryId={page.params.categoryId} />
} else if (page.route === 'post') {
return <PostPage postId={page.params.postId} />
}
}
npm install nanostores @nanostores/router
See Nano Stores docs about using the store and subscribing to store’s changes in UI frameworks.
Routes is an object of route’s name to route pattern:
createRouter({
route1: '/',
route2: '/path/:var1/and/:var2',
route3: [/\/posts\/(draft|new)\/(\d+)/, (type, id) => ({ type, id })]
})
For string patterns you can use :name
for variable parts. To make the
parameter optional, mark it with the ?
modifier:
createRouter({
routeName: '/profile/:id?/:tab?'
})
Routes can have RegExp patterns. They should be an array with function,
which convert ()
groups to key-value map.
For TypeScript, you need specify interface with variable names, used in routes.
interface Routes {
routeName: 'var1' | 'var2'
}
createRouter<Routes>({
routeName: '/path/:var1/and/:var2'
})
Using getPagePath()
avoids hard coding URL in templates. It is better
to use the router as a single place of truth.
import { getPagePath } from '@nanostores/router'
…
<a href={getPagePath(router, 'post', { categoryId: 'guides', id: '10' })}>
If you need to change URL programmatically you can use openPage
or redirectPage
:
import { openPage, redirectPage } from '@nanostores/router'
function requireLogin () {
openPage(router, 'login')
}
function onLoginSuccess() {
// Replace login route, so we don’t face it on back navigation
redirectPage(router, 'home')
}
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:
if (isServer) {
router.open('/posts/demo/1')
}