https://github.com/nanostores/router
A tiny (673 bytes) router for Nano Stores state manager
https://github.com/nanostores/router
Last synced: 7 months ago
JSON representation
A tiny (673 bytes) router for Nano Stores state manager
- Host: GitHub
- URL: https://github.com/nanostores/router
- Owner: nanostores
- License: mit
- Created: 2021-06-24T01:08:53.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2025-04-10T22:07:00.000Z (9 months ago)
- Last Synced: 2025-04-14T15:02:36.868Z (8 months ago)
- Language: TypeScript
- Homepage:
- Size: 867 KB
- Stars: 259
- Watchers: 2
- Forks: 20
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
- awesome-tiny-js - @nanostores/router - Routes as a nanostores store (framework-agnostic), <img align="top" height="24" src="./img/nanostoresrouter.svg"> (Routers and URL Utils / Reactive Programming)
README
# Nano Stores Router

A tiny URL router for [Nano Stores](https://github.com/nanostores/nanostores)
state manager.
- **Small.** 712 bytes (minified and brotlied). Zero dependencies.
- Good **TypeScript** support.
- Framework agnostic. Can be used with **React**, **Preact**, **Vue**,
**Svelte**, **Angular**, **Solid.js**, and vanilla JS.
Since Nano Stores promote moving logic to store, the router is a store,
not a component in UI framework like React.
```ts
// stores/router.ts
import { createRouter } from '@nanostores/router'
export const $router = createRouter({
home: '/',
list: '/posts/:category',
post: '/posts/:category/:post'
})
```
Store in active mode listen for `` clicks on `document.body` and Back button
in browser.
```tsx
// components/layout.tsx
import { useStore } from '@nanostores/react'
import { $router } from '../stores/router.js'
export const Layout = () => {
const page = useStore($router)
if (!page) {
return
} else if (page.route === 'home') {
return
} else if (page.route === 'list') {
return
} else if (page.route === 'post') {
return
}
}
```
---
Made at Evil Martians, product consulting for developer tools.
---
## Install
```sh
npm install nanostores @nanostores/router
```
## Usage
See [Nano Stores docs](https://github.com/nanostores/nanostores#guide)
about using the store and subscribing to store’s changes in UI frameworks.
### Routes
Routes is an object of route’s name to route pattern:
```ts
createRouter({
route1: '/',
route2: '/path/:var1/and/:var2',
route3: /\/posts\/(?draft|new)\/(?\d+)/
})
```
For string patterns you can use `:name` for variable parts. To make the
parameter optional, mark it with the `?` modifier:
```ts
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, router parameters will be converted to types automatically.
You need to use TypeScript ≥5.x.
```ts
createRouter({
routeName: '/path/:var1/and/:var2',
routeName2: [/path2/, () => ({ num: 1, str: '' })]
})
/**
* Params will be inferred as:
* {
* routeName: { var1: string, var2: string },
* routeName2: { num: number, str: string }
* }
*/
```
### Search Query Routing
Router value contains parsed URL search params (like `?sort=name`):
```js
createRouter({ home: '/posts/:category' })
location.href = '/posts/general?sort=name'
router.get() //=> {
// path: '/posts/general',
// route: 'list',
// params: { category: 'general' },
// search: { sort: 'name' },
// hash: ''
// }
```
To disable the automatic parsing of search params in routes you need
to set `search` option. Router will now treat search query like `?a=1&b=2`
as a string. Parameters order will be critical.
```js
createRouter({ home: '/posts?page=general' }, { search: true })
location.href = '/posts/?page=general'
router.get() //=> {
// path: '/posts?page=general',
// route: 'list',
// params: { },
// search: { },
// hash: ''
// }
```
### Hash Routing
Router’s value has current `location.hash` and router updates its value
on hash changes.
```js
location.href = '/posts/general#dialog'
router.get() //=> {
// path: '/posts/general',
// route: 'list',
// params: { category: 'general' },
// search: {},
// hash: '#dialog'
// }
```
### Clicks Tracking
By default, router and `?search` params store will add `click` event listener
on `window` to track links clicks.
To disable click tracking for specific link, add `target="_self"` to link tag:
```html
Posts
```
You can disable this behavior by `links: false` options and create custom
`` component.
```js
export const $router = createRouter({ … }, { links: false })
function onClick (e) {
e.preventDefault()
$router.open(new Url(e.target.href).pathname)
}
export const Link = (props) => {
return
}
```
### URL Generation
Using `getPagePath()` avoids hard coding URL in templates. It is better
to use the router as a single place of truth.
```tsx
import { getPagePath } from '@nanostores/router'
…
```
If you need to change URL programmatically you can use `openPage`
or `redirectPage`:
```ts
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')
}
```
All functions accept search params as last argument:
```tsx
getPagePath($router, 'list', { category: 'guides' }, { sort: 'name' })
//=> '/posts/guides?sort=name'
```
### Server-Side Rendering
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:
```js
if (isServer) {
$router.open('/posts/demo/1')
}
```