{"id":13496330,"url":"https://github.com/nanostores/router","last_synced_at":"2025-05-15T09:07:52.094Z","repository":{"id":37419290,"uuid":"379767706","full_name":"nanostores/router","owner":"nanostores","description":"A tiny (673 bytes) router for Nano Stores state manager","archived":false,"fork":false,"pushed_at":"2025-04-10T22:07:00.000Z","size":888,"stargazers_count":259,"open_issues_count":3,"forks_count":20,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-14T15:02:36.868Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nanostores.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"ai"}},"created_at":"2021-06-24T01:08:53.000Z","updated_at":"2025-04-10T22:07:04.000Z","dependencies_parsed_at":"2023-12-29T13:26:57.562Z","dependency_job_id":"2c426cad-a75d-4057-9318-7547c21d42e8","html_url":"https://github.com/nanostores/router","commit_stats":{"total_commits":111,"total_committers":10,"mean_commits":11.1,"dds":"0.15315315315315314","last_synced_commit":"d3b84c518fe4e09aa084e326582c27fef88f6414"},"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanostores%2Frouter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanostores%2Frouter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanostores%2Frouter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanostores%2Frouter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nanostores","download_url":"https://codeload.github.com/nanostores/router/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254310515,"owners_count":22049469,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T19:01:46.067Z","updated_at":"2025-05-15T09:07:47.086Z","avatar_url":"https://github.com/nanostores.png","language":"TypeScript","funding_links":["https://github.com/sponsors/ai"],"categories":["TypeScript","Routers and URL Utils"],"sub_categories":["Reactive Programming"],"readme":"# Nano Stores Router\n\n\u003cimg align=\"right\" width=\"92\" height=\"92\" title=\"Nano Stores logo\"\n     src=\"https://nanostores.github.io/nanostores/logo.svg\"\u003e\n\nA tiny URL router for [Nano Stores](https://github.com/nanostores/nanostores)\nstate manager.\n\n- **Small.** 712 bytes (minified and brotlied). Zero dependencies.\n- Good **TypeScript** support.\n- Framework agnostic. Can be used with **React**, **Preact**, **Vue**,\n  **Svelte**, **Angular**, **Solid.js**, and vanilla JS.\n\nSince Nano Stores promote moving logic to store, the router is a store,\nnot a component in UI framework like React.\n\n```ts\n// stores/router.ts\nimport { createRouter } from '@nanostores/router'\n\nexport const $router = createRouter({\n  home: '/',\n  list: '/posts/:category',\n  post: '/posts/:category/:post'\n})\n```\n\nStore in active mode listen for `\u003ca\u003e` clicks on `document.body` and Back button\nin browser.\n\n```tsx\n// components/layout.tsx\nimport { useStore } from '@nanostores/react'\n\nimport { $router } from '../stores/router.js'\n\nexport const Layout = () =\u003e {\n  const page = useStore($router)\n\n  if (!page) {\n    return \u003cError404 /\u003e\n  } else if (page.route === 'home') {\n    return \u003cHomePage /\u003e\n  } else if (page.route === 'list') {\n    return \u003cListPage category={page.params.category} filters={page.search} /\u003e\n  } else if (page.route === 'post') {\n    return \u003cPostPage post={page.params.post} /\u003e\n  }\n}\n```\n\n---\n\n\u003cimg src=\"https://cdn.evilmartians.com/badges/logo-no-label.svg\" alt=\"\" width=\"22\" height=\"16\" /\u003e  Made at \u003cb\u003e\u003ca href=\"https://evilmartians.com/devtools?utm_source=nanostores-router\u0026utm_campaign=devtools-button\u0026utm_medium=github\"\u003eEvil Martians\u003c/a\u003e\u003c/b\u003e, product consulting for \u003cb\u003edeveloper tools\u003c/b\u003e.\n\n---\n\n## Install\n\n```sh\nnpm install nanostores @nanostores/router\n```\n\n## Usage\n\nSee [Nano Stores docs](https://github.com/nanostores/nanostores#guide)\nabout using the store and subscribing to store’s changes in UI frameworks.\n\n### Routes\n\nRoutes is an object of route’s name to route pattern:\n\n```ts\ncreateRouter({\n  route1: '/',\n  route2: '/path/:var1/and/:var2',\n  route3: /\\/posts\\/(?\u003ctype\u003edraft|new)\\/(?\u003cid\u003e\\d+)/\n})\n```\n\nFor string patterns you can use `:name` for variable parts. To make the\nparameter optional, mark it with the `?` modifier:\n\n```ts\ncreateRouter({\n  routeName: '/profile/:id?/:tab?'\n})\n```\n\nRoutes can have RegExp patterns. They should be an array with function,\nwhich convert `()` groups to key-value map.\n\nFor TypeScript, router parameters will be converted to types automatically.\nYou need to use TypeScript ≥5.x.\n\n```ts\ncreateRouter({\n  routeName: '/path/:var1/and/:var2',\n  routeName2: [/path2/, () =\u003e ({ num: 1, str: '' })]\n})\n\n/**\n * Params will be inferred as:\n * {\n *   routeName: { var1: string, var2: string },\n *   routeName2: { num: number, str: string }\n * }\n */\n```\n\n### Search Query Routing\n\nRouter value contains parsed URL search params (like `?sort=name`):\n\n```js\ncreateRouter({ home: '/posts/:category' })\n\nlocation.href = '/posts/general?sort=name'\nrouter.get() //=\u003e {\n//                   path: '/posts/general',\n//                   route: 'list',\n//                   params: { category: 'general' },\n//                   search: { sort: 'name' },\n//                   hash: ''\n//                 }\n```\n\nTo disable the automatic parsing of search params in routes you need\nto set `search` option. Router will now treat search query like `?a=1\u0026b=2`\nas a string. Parameters order will be critical.\n\n```js\ncreateRouter({ home: '/posts?page=general' }, { search: true })\n\nlocation.href = '/posts/?page=general'\nrouter.get() //=\u003e {\n//                   path: '/posts?page=general',\n//                   route: 'list',\n//                   params: { },\n//                   search: { },\n//                   hash: ''\n//                 }\n```\n\n### Hash Routing\n\nRouter’s value has current `location.hash` and router updates its value\non hash changes.\n\n```js\nlocation.href = '/posts/general#dialog'\nrouter.get() //=\u003e {\n//                   path: '/posts/general',\n//                   route: 'list',\n//                   params: { category: 'general' },\n//                   search: {},\n//                   hash: '#dialog'\n//                 }\n```\n\n### Clicks Tracking\n\nBy default, router and `?search` params store will add `click` event listener\non `window` to track links clicks.\n\nTo disable click tracking for specific link, add `target=\"_self\"` to link tag:\n\n```html\n\u003ca href=\"/posts\" target=\"_self\"\u003ePosts\u003c/a\u003e\n```\n\nYou can disable this behavior by `links: false` options and create custom\n`\u003cLink\u003e` component.\n\n```js\nexport const $router = createRouter({ … }, { links: false })\n\nfunction onClick (e) {\n  e.preventDefault()\n  $router.open(new Url(e.target.href).pathname)\n}\n\nexport const Link = (props) =\u003e {\n  return \u003ca onClick={onClick} {...props}\u003e\u003c/a\u003e\n}\n```\n\n### URL Generation\n\nUsing `getPagePath()` avoids hard coding URL in templates. It is better\nto use the router as a single place of truth.\n\n```tsx\nimport { getPagePath } from '@nanostores/router'\n\n…\n  \u003ca href={getPagePath($router, 'post', { category: 'guides', post: '10' })}\u003e\n```\n\nIf you need to change URL programmatically you can use `openPage`\nor `redirectPage`:\n\n```ts\nimport { openPage, redirectPage } from '@nanostores/router'\n\nfunction requireLogin() {\n  openPage($router, 'login')\n}\n\nfunction onLoginSuccess() {\n  // Replace login route, so we don’t face it on back navigation\n  redirectPage($router, 'home')\n}\n```\n\nAll functions accept search params as last argument:\n\n```tsx\ngetPagePath($router, 'list', { category: 'guides' }, { sort: 'name' })\n//=\u003e '/posts/guides?sort=name'\n```\n\n### Server-Side Rendering\n\nRouter can be used in Node environment without `window` and `location`.\nIn this case, it will always return route to `/` path.\n\nYou can manually set any other route:\n\n```js\nif (isServer) {\n  $router.open('/posts/demo/1')\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnanostores%2Frouter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnanostores%2Frouter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnanostores%2Frouter/lists"}