{"id":13417809,"url":"https://github.com/nanostores/nanostores","last_synced_at":"2026-03-15T01:31:46.379Z","repository":{"id":37326121,"uuid":"304120355","full_name":"nanostores/nanostores","owner":"nanostores","description":"A tiny (286 bytes) state manager for React/RN/Preact/Vue/Svelte with many atomic tree-shakable stores","archived":false,"fork":false,"pushed_at":"2025-04-27T07:22:31.000Z","size":2648,"stargazers_count":6092,"open_issues_count":24,"forks_count":114,"subscribers_count":24,"default_branch":"main","last_synced_at":"2025-04-30T07:40:00.285Z","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":"2020-10-14T19:51:28.000Z","updated_at":"2025-04-29T23:22:55.000Z","dependencies_parsed_at":"2023-10-16T08:24:49.026Z","dependency_job_id":"b24cf41a-dcdd-46b3-8e76-6fd38ed30517","html_url":"https://github.com/nanostores/nanostores","commit_stats":{"total_commits":747,"total_committers":30,"mean_commits":24.9,"dds":"0.20749665327978584","last_synced_commit":"83273fab8ddc9230e7a2d05001d6cd0c0c0da8ce"},"previous_names":["logux/state"],"tags_count":55,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanostores%2Fnanostores","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanostores%2Fnanostores/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanostores%2Fnanostores/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanostores%2Fnanostores/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nanostores","download_url":"https://codeload.github.com/nanostores/nanostores/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252372162,"owners_count":21737473,"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-30T22:00:53.146Z","updated_at":"2026-03-15T01:31:46.369Z","avatar_url":"https://github.com/nanostores.png","language":"TypeScript","funding_links":["https://github.com/sponsors/ai"],"categories":["What Do I Use...","TypeScript","Stores","Uncategorized","💻 Web Development","others","State-management","Web Frontend","🗂️ Use Cases","State Managers"],"sub_categories":["If I want to share state between components?","Uncategorized","JS Libraries \u0026 Utilities","nanostores-router"],"readme":"# Nano Stores\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 state manager for **React**, **React Native**, **Preact**, **Vue**,\n**Svelte**, **Solid**, **Lit**, **Angular**, and vanilla JS.\nIt uses **many atomic stores** and direct manipulation.\n\n* **Small.** Between 265 and 797 bytes (minified and brotlied).\n  Zero dependencies. It uses [Size Limit] to control size.\n* **Fast.** With small atomic and derived stores, you do not need to call\n  the selector function for all components on every store change.\n* **Tree Shakable.** A chunk contains only stores used by components\n  in the chunk.\n* Designed to move logic from components to stores.\n* Good **TypeScript** support.\n\n```ts\n// store/users.ts\nimport { atom } from 'nanostores'\n\nexport const $users = atom\u003cUser[]\u003e([])\n\nexport function addUser(user: User) {\n  $users.set([...$users.get(), user]);\n}\n```\n\n```ts\n// store/admins.ts\nimport { computed } from 'nanostores'\nimport { $users } from './users.ts'\n\nexport const $admins = computed($users, users =\u003e users.filter(i =\u003e i.isAdmin))\n```\n\n```tsx\n// components/admins.tsx\nimport { useStore } from '@nanostores/react'\nimport { $admins } from '../stores/admins.ts'\n\nexport const Admins = () =\u003e {\n  const admins = useStore($admins)\n  return (\n    \u003cul\u003e\n      {admins.map(user =\u003e \u003cUserItem user={user} /\u003e)}\n    \u003c/ul\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\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[Size Limit]: https://github.com/ai/size-limit\n\n\n## Table of Contents\n\n- [Table of Contents](#table-of-contents)\n- [Install](#install)\n- [Smart Stores](#smart-stores)\n- [Devtools](#devtools)\n- [Guide](#guide)\n  - [Atoms](#atoms)\n  - [Maps](#maps)\n  - [Lazy Stores](#lazy-stores)\n  - [Computed Stores](#computed-stores)\n  - [Effects](#effects)\n  - [Map Creator](#map-creator)\n  - [Tasks](#tasks)\n  - [Store Events](#store-events)\n- [Integration](#integration)\n  - [React \\\u0026 Preact](#react--preact)\n  - [Vue](#vue)\n  - [Svelte](#svelte)\n  - [Solid](#solid)\n  - [Lit](#lit)\n  - [Angular](#angular)\n  - [Vanilla JS](#vanilla-js)\n  - [Server-Side Rendering](#server-side-rendering)\n  - [Tests](#tests)\n- [Best Practices](#best-practices)\n  - [Move Logic from Components to Stores](#move-logic-from-components-to-stores)\n  - [Separate changes and reaction](#separate-changes-and-reaction)\n  - [Reduce `get()` usage outside of tests](#reduce-get-usage-outside-of-tests)\n- [Known Issues](#known-issues)\n  - [ESM](#esm)\n\n\n## Install\n\n```sh\nnpm install nanostores\n```\n\n\n## Smart Stores\n\n* [Persistent](https://github.com/nanostores/persistent) store to save data\n  to `localStorage` and synchronize changes between browser tabs.\n* [Router](https://github.com/nanostores/router) store to parse URL\n  and implements SPA navigation.\n* [Media Query](https://github.com/nanostores/media-query) store sync value\n  with media query.\n* [Deep Map](https://github.com/nanostores/deepmap) store to put big nested object/arrays and change keys by path.\n* [I18n](https://github.com/nanostores/i18n) library based on stores\n  to make application translatable.\n* [Query](https://github.com/nanostores/query) store that helps you with smart\n  remote data fetching.\n* [Logux Client](https://github.com/logux/client): stores with WebSocket\n  sync and CRDT conflict resolution.\n* [Immer](https://github.com/illuxiza/nanostores-immer) plugin to\n  enable immutable state updates using Immer.\n* [qs](https://github.com/VdustR/nanostores-qs) manage the query string in the URL.\n\n\n## Devtools\n\n* [Logger](https://github.com/nanostores/logger) of lifecycle, changes\n  in the browser console.\n* [Vue Devtools](https://github.com/nanostores/vue#devtools) plugin that detects\n  stores and attaches them to devtools inspectors and timeline.\n\n\n## Guide\n\n### Atoms\n\nAtom store can be used to store strings, numbers, arrays.\n\nYou can use it for objects too if you want to prohibit key changes\nand allow only replacing the whole object (like we do in [router]).\n\nTo create it call `atom(initial)` and pass initial value as a first argument.\n\n```ts\nimport { atom } from 'nanostores'\n\nexport const $counter = atom(0)\n```\n\nIn TypeScript, you can optionally pass value type as type parameter.\n\n```ts\nexport type LoadingStateValue = 'empty' | 'loading' | 'loaded'\n\nexport const $loadingState = atom\u003cLoadingStateValue\u003e('empty')\n```\n\nThen you can use `StoreValue\u003cStore\u003e` helper to get store’s value type\nin TypeScript:\n\n```ts\nimport type { StoreValue } from 'nanostores'\n\ntype Value = StoreValue\u003ctypeof $loadingState\u003e //=\u003e LoadingStateValue\n```\n\n`store.get()` will return store’s current value.\n`store.set(nextValue)` will change value.\n\n```ts\n$counter.set($counter.get() + 1)\n```\n\n`store.subscribe(cb)` and `store.listen(cb)` can be used to subscribe\nfor the changes in vanilla JS. For [React](#react--preact)/[Vue](#vue)\nwe have extra special helpers `useStore` to re-render the component on\nany store changes.\n\nListener callbacks will receive the updated value as a first argument\nand the previous value as a second argument.\n\n```ts\nconst unbindListener = $counter.subscribe((value, oldValue) =\u003e {\n  console.log(`counter value changed from ${oldValue} to ${value}`)\n})\n```\n\n`store.subscribe(cb)` in contrast with `store.listen(cb)` also call listeners\nimmediately during the subscription.\nNote that the initial call for `store.subscribe(cb)` will not have any\nprevious value and `oldValue` will be `undefined`.\n\nSee also [`effect()`](#effects) if you want to subscribe to multiple stores.\n\n[router]: https://github.com/nanostores/router\n\n\n### Maps\n\nMap store can be used to store objects with one level of depth and change keys\nin this object.\n\nTo create map store call `map(initial)` function with initial object.\n\n```ts\nimport { map } from 'nanostores'\n\nexport const $profile = map({\n  name: 'anonymous'\n})\n```\n\nIn TypeScript, you can pass type parameter with store’s type:\n\n```ts\nexport interface ProfileValue {\n  name: string,\n  email?: string\n}\n\nexport const $profile = map\u003cProfileValue\u003e({\n  name: 'anonymous'\n})\n```\n\n`store.set(object)` or `store.setKey(key, value)` methods will change the store.\n\n```ts\n$profile.setKey('name', 'Kazimir Malevich')\n```\n\nSetting `undefined` will remove optional key:\n\n```ts\n$profile.setKey('email', undefined)\n```\n\nStore’s listeners will receive third argument with changed key.\n\n```ts\n$profile.listen((profile, oldProfile, changed) =\u003e {\n  console.log(`${changed} new value ${profile[changed]}`)\n})\n```\n\nYou can also listen for specific keys of the store being changed, using\n`listenKeys` and `subscribeKeys`.\n\n```ts\nlistenKeys($profile, ['name'], (value, oldValue, changed) =\u003e {\n  console.log(`$profile.Name new value ${value.name}`)\n})\n```\n\n`subscribeKeys(store, keys, cb)` in contrast with `listenKeys(store, keys, cb)`\nalso call listeners immediately during the subscription.\nPlease note that when using subscribe for store changes, the initial evaluation\nof the callback has undefined old value and changed key.\n\n\n### Lazy Stores\n\nA unique feature of Nano Stores is that every state has two modes:\n\n* **Mount:** when one or more listeners is mounted to the store.\n* **Disabled:** when store has no listeners.\n\nNano Stores was created to move logic from components to the store.\nStores can listen for URL changes or establish network connections.\nMount/disabled modes allow you to create lazy stores, which will use resources\nonly if store is really used in the UI.\n\n`onMount` sets callback for mount and disabled states.\n\n```ts\nimport { onMount } from 'nanostores'\n\nonMount($profile, () =\u003e {\n  // Mount mode\n  return () =\u003e {\n    // Disabled mode\n  }\n})\n```\n\nFor performance reasons, store will move to disabled mode with 1-second delay\nafter last listener unsubscribing.\n\nCall `keepMount()` to test store’s lazy initializer in tests and `cleanStores`\nto unmount them after test.\n\n```js\nimport { cleanStores, keepMount } from 'nanostores'\nimport { $profile } from './profile.js'\n\nafterEach(() =\u003e {\n  cleanStores($profile)\n})\n\nit('is anonymous from the beginning', () =\u003e {\n  keepMount($profile)\n  // Checks\n})\n```\n\n\n### Computed Stores\n\nComputed store is based on other store’s value.\n\n```ts\nimport { computed } from 'nanostores'\nimport { $users } from './users.js'\n\nexport const $admins = computed($users, users =\u003e {\n  // This callback will be called on every `users` changes\n  return users.filter(user =\u003e user.isAdmin)\n})\n```\n\nAn async function can be evaluated by using `task()`.\n\n```js\nimport { computed, task } from 'nanostores'\n\nimport { $userId } from './users.js'\n\nexport const $user = computed($userId, userId =\u003e task(async () =\u003e {\n  const response = await fetch(`https://my-api/users/${userId}`)\n  return response.json()\n}))\n```\n\nBy default, `computed` stores update _each_ time any of their dependencies\ngets updated. If you are fine with waiting until the end of a tick, you can\nuse `batched`. The only difference with `computed` is that it will wait until\nthe end of a tick to update itself.\n\n```ts\nimport { batched } from 'nanostores'\n\nconst $sortBy = atom('id')\nconst $categoryId = atom('')\n\nexport const $link = batched([$sortBy, $categoryId], (sortBy, categoryId) =\u003e {\n  return `/api/entities?sortBy=${sortBy}\u0026categoryId=${categoryId}`\n})\n\n// `batched` will update only once even you changed two stores\nexport function resetFilters () {\n  $sortBy.set('date')\n  $categoryIdFilter.set('1')\n}\n```\n\nBoth `computed` and `batched` can be calculated from multiple stores:\n\n```ts\nimport { $lastVisit } from './lastVisit.js'\nimport { $posts } from './posts.js'\n\nexport const $newPosts = computed([$lastVisit, $posts], (lastVisit, posts) =\u003e {\n  return posts.filter(post =\u003e post.publishedAt \u003e lastVisit)\n})\n```\n\n\n### Effects\n\n`effect` subscribes for multiple atoms at once.\n\n`effect` runs its callback on the start, with initial values, as well as\non any stores change. If callback returns cleanup function it will be performed\nbefore next callback run. Besides that, `effect` returns own cleanup function,\nwhich allows cancelling the whole effect.\n\n```js\nconst $enabled = atom(true)\nconst $interval = atom(1000)\n\nconst cancelPing = effect([$enabled, $interval], (enabled, interval) =\u003e {\n  if (!enabled) return\n\n  const intervalId = setInterval(() =\u003e {\n    sendPing()\n  }, interval)\n\n  return () =\u003e {\n    clearInterval(intervalId)\n  }\n})\n```\n\n### Map Creator\n\nIf you have many similar stores (for instance, in advanced database ORM),\nyou can define map creator (like a “class” in OOP).\n\n```js\nconst User = mapCreator((store, id) =\u003e {\n  store.set({ id, isLoading: true })\n  fetchUser(id).then(data =\u003e {\n    store.set({ id, isLoading: false, data })\n  })\n})\n\nlet user1 = User('1')\n```\n\n\n### Tasks\n\n`startTask()` and `task()` can be used to mark all async operations\nduring store initialization.\n\n```ts\nimport { task } from 'nanostores'\n\nonMount($post, () =\u003e {\n  task(async () =\u003e {\n    $post.set(await loadPost())\n  })\n})\n```\n\nYou can wait for all ongoing tasks end in tests or SSR with `await allTasks()`.\n\n```jsx\nimport { allTasks } from 'nanostores'\n\n$post.listen(() =\u003e {}) // Move store to active mode to start data loading\nawait allTasks()\n\nconst html = ReactDOMServer.renderToString(\u003cApp /\u003e)\n```\n\n\n### Store Events\n\nEach store has a few events, which you listen:\n\n* `onMount(store, cb)`: first listener was subscribed with debounce.\n  We recommend to always use `onMount` instead of `onStart + onStop`,\n  because it has a short delay to prevent flickering behavior.\n* `onStart(store, cb)`: first listener was subscribed. Low-level method.\n  It is better to use `onMount` for simple lazy stores.\n* `onStop(store, cb)`: last listener was unsubscribed. Low-level method.\n  It is better to use `onMount` for simple lazy stores.\n* `onSet(store, cb)`: before applying any changes to the store.\n* `onNotify(store, cb)`: before notifying store’s listeners about changes.\n\n`onSet` and `onNotify` events has `abort()` function to prevent changes\nor notification.\n\n```ts\nimport { onSet } from 'nanostores'\n\nonSet($store, ({ newValue, abort }) =\u003e {\n  if (!validate(newValue)) {\n    abort()\n  }\n})\n```\n\nEvent listeners can communicate with `payload.shared` object.\n\n\n## Integration\n\n### React \u0026 Preact\n\nUse [`@nanostores/react`] or [`@nanostores/preact`] package\nand `useStore()` hook to get store’s value and re-render component\non store’s changes.\n\n```tsx\nimport { useStore } from '@nanostores/react' // or '@nanostores/preact'\nimport { $profile } from '../stores/profile.js'\n\nexport const Header = ({ postId }) =\u003e {\n  const profile = useStore($profile)\n  return \u003cheader\u003eHi, {profile.name}\u003c/header\u003e\n}\n```\n\n[`@nanostores/preact`]: https://github.com/nanostores/preact\n[`@nanostores/react`]: https://github.com/nanostores/react\n\n\n### Vue\n\nUse [`@nanostores/vue`] and `useStore()` composable function\nto get store’s value and re-render component on store’s changes.\n\n```vue\n\u003cscript setup\u003e\nimport { useStore } from '@nanostores/vue'\nimport { $profile } from '../stores/profile.js'\n\nconst props = defineProps(['postId'])\n\nconst profile = useStore($profile)\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cheader\u003eHi, {{ profile.name }}\u003c/header\u003e\n\u003c/template\u003e\n```\n\n[`@nanostores/vue`]: https://github.com/nanostores/vue\n\n\n### Svelte\n\nEvery store implements [Svelte's store contract]. Put `$` before store variable\nto get store’s value and subscribe for store’s changes.\n\n```svelte\n\u003cscript\u003e\n  import { profile } from '../stores/profile.js'\n\u003c/script\u003e\n\n\u003cheader\u003eHi, {$profile.name}\u003c/header\u003e\n```\n\nIn other frameworks, Nano Stores promote code style to use `$` prefixes\nfor store’s names. But in Svelte it has a special meaning, so we recommend\nto not follow this code style here.\n\n[Svelte's store contract]: https://svelte.dev/docs/svelte-components#script-4-prefix-stores-with-$-to-access-their-values\n\n\n### Solid\n\nUse [`@nanostores/solid`] and `useStore()` composable function\nto get store’s value and re-render component on store’s changes.\n\n```js\nimport { useStore } from '@nanostores/solid'\nimport { $profile } from '../stores/profile.js'\n\nexport function Header({ postId }) {\n  const profile = useStore($profile)\n  return \u003cheader\u003eHi, {profile().name}\u003c/header\u003e\n}\n```\n\n[`@nanostores/solid`]: https://github.com/nanostores/solid\n\n\n### Lit\n\nUse [`@nanostores/lit`] and `StoreController` reactive controller\nto get store’s value and re-render component on store’s changes.\n\n```ts\nimport { StoreController } from '@nanostores/lit'\nimport { $profile } from '../stores/profile.js'\n\n@customElement('my-header')\nclass MyElement extends LitElement {\n  @property()\n\n  private profileController = new StoreController(this, $profile)\n\n  render() {\n    return html`\u003cheader\u003eHi, ${profileController.value.name}\u003c/header\u003e`\n  }\n}\n```\n\n[`@nanostores/lit`]: https://github.com/nanostores/lit\n\n\n### Angular\n\nUse [`@nanostores/angular`] and `NanostoresService` with `useStore()`\nmethod to get store’s value and subscribe for store’s changes.\n\n```ts\n// NgModule:\nimport { NANOSTORES, NanostoresService } from '@nanostores/angular';\n\n@NgModule({\n  providers: [{ provide: NANOSTORES, useClass: NanostoresService }]\n})\n```\n\n```tsx\n// Component:\nimport { Component } from '@angular/core'\nimport { NanostoresService } from '@nanostores/angular'\nimport { Observable, switchMap } from 'rxjs'\n\nimport { profile } from '../stores/profile'\nimport { IUser, User } from '../stores/user'\n\n@Component({\n  selector: \"app-root\",\n  template: '\u003cp *ngIf=\"(currentUser$ | async) as user\"\u003e{{ user.name }}\u003c/p\u003e'\n})\nexport class AppComponent {\n  currentUser$: Observable\u003cIUser\u003e = this.nanostores.useStore(profile)\n    .pipe(switchMap(userId =\u003e this.nanostores.useStore(User(userId))))\n\n  constructor(private nanostores: NanostoresService) { }\n}\n```\n\n[`@nanostores/angular`]: https://github.com/nanostores/angular\n\n\n### Vanilla JS\n\n`Store#subscribe()` calls callback immediately and subscribes to store changes.\nIt passes store’s value to callback.\n\n```js\nimport { $profile } from '../stores/profile.js'\n\n$profile.subscribe(profile =\u003e {\n  console.log(`Hi, ${profile.name}`)\n})\n```\n\n`Store#listen(cb)` in contrast, calls only on next store change. It could be\nuseful for a multiple stores listeners.\n\n```js\nfunction render () {\n  console.log(`${$post.get().title} for ${$profile.get().name}`)\n}\n\n$profile.listen(render)\n$post.listen(render)\nrender()\n```\n\nSee also `listenKeys(store, keys, cb)` to listen for specific keys changes\nin the map.\n\n\n### Server-Side Rendering\n\nNano Stores support SSR. Use standard strategies.\n\n```js\nif (isServer) {\n  $settings.set(initialSettings)\n  $router.open(renderingPageURL)\n}\n```\n\nYou can wait for async operations (for instance, data loading\nvia isomorphic `fetch()`) before rendering the page:\n\n```jsx\nimport { allTasks } from 'nanostores'\n\n$post.listen(() =\u003e {}) // Move store to active mode to start data loading\nawait allTasks()\n\nconst html = ReactDOMServer.renderToString(\u003cApp /\u003e)\n```\n\n\n### Tests\n\nAdding an empty listener by `keepMount(store)` keeps the store\nin active mode during the test. `cleanStores(store1, store2, …)` cleans\nstores used in the test.\n\n```ts\nimport { cleanStores, keepMount } from 'nanostores'\nimport { $profile } from './profile.js'\n\nafterEach(() =\u003e {\n  cleanStores($profile)\n})\n\nit('is anonymous from the beginning', () =\u003e {\n  keepMount($profile)\n  expect($profile.get()).toEqual({ name: 'anonymous' })\n})\n```\n\nYou can use `allTasks()` to wait all async operations in stores.\n\n```ts\nimport { allTasks } from 'nanostores'\n\nit('saves user', async () =\u003e {\n  saveUser()\n  await allTasks()\n  expect(analyticsEvents.get()).toEqual(['user:save'])\n})\n```\n\n\n## Best Practices\n\n### Move Logic from Components to Stores\n\nStores are not only to keep values. You can use them to track time, to load data\nfrom server.\n\n```ts\nimport { atom, onMount } from 'nanostores'\n\nexport const $currentTime = atom\u003cnumber\u003e(Date.now())\n\nonMount($currentTime, () =\u003e {\n  $currentTime.set(Date.now())\n  const updating = setInterval(() =\u003e {\n    $currentTime.set(Date.now())\n  }, 1000)\n  return () =\u003e {\n    clearInterval(updating)\n  }\n})\n```\n\nUse derived stores to create chains of reactive computations.\n\n```ts\nimport { computed } from 'nanostores'\nimport { $currentTime } from './currentTime.js'\n\nconst appStarted = Date.now()\n\nexport const $userInApp = computed($currentTime, currentTime =\u003e {\n  return currentTime - appStarted\n})\n```\n\nWe recommend moving all logic, which is not highly related to UI, to the stores.\nLet your stores track URL routing, validation, sending data to a server.\n\nWith application logic in the stores, it is much easier to write and run tests.\nIt is also easy to change your UI framework. For instance, add React Native\nversion of the application.\n\n\n### Separate changes and reaction\n\nUse a separated listener to react on new store’s value, not an action function\nwhere you change this store.\n\n```diff\n  function increase() {\n    $counter.set($counter.get() + 1)\n-   printCounter(store.get())\n  }\n\n+ $counter.listen(counter =\u003e {\n+   printCounter(counter)\n+ })\n```\n\nAn action function is not the only way for store to a get new value.\nFor instance, persistent store could get the new value from another browser tab.\n\nWith this separation your UI will be ready to any source of store’s changes.\n\n\n### Reduce `get()` usage outside of tests\n\n`get()` returns current value, and it is a good solution for tests.\n\nBut it is better to use `useStore()`, `$store`, or `Store#subscribe()` in UI\nto subscribe to store changes and always render the actual data.\n\n```diff\n- const { userId } = $profile.get()\n+ const { userId } = useStore($profile)\n```\n\n\n## Known Issues\n\n### ESM\n\nNano Stores use ESM-only package. You need to use ES modules\nin your application to import Nano Stores.\n\nIn Next.js ≥11.1 you can alternatively use the [`esmExternals`] config option.\n\nFor old Next.js you need to use [`next-transpile-modules`] to fix\nlack of ESM support in Next.js.\n\n[`next-transpile-modules`]: https://www.npmjs.com/package/next-transpile-modules\n[`esmExternals`]: https://nextjs.org/blog/next-11-1#es-modules-support\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnanostores%2Fnanostores","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnanostores%2Fnanostores","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnanostores%2Fnanostores/lists"}