Skip to content

API reference

Reference for stores, subscriptions, lifecycle hooks and TypeScript types.

function atom<Value, StoreExt>(...args): PreinitializedWritableAtom<Value> & StoreExt;

Create store with atomic value. It could be a string or an object, which you will replace completely.

If you want to change keys in the object inside store, use map.

import { atom, onMount } from 'nanostores'
// Initial value
export const $router = atom({ path: '', page: 'home' })
function parse () {
$router.set({ path: location.pathname, page: parse(location.pathname) })
}
// Listen for URL changes on first store’s listener.
onMount($router, () => {
parse()
window.addEventListener('popstate', parse)
return () => {
window.removeEventListener('popstate', parse)
}
})

Value

StoreExt = object

…undefined extends Value ? [] | [Value] : [Value]

PreinitializedWritableAtom<Value> & StoreExt

The store object with methods to subscribe.

function map<Value, StoreExt>(value): PreinitializedMapStore<Value> & StoreExt;

Create map store with an initial value. Map store is a store with key-value object as a store value.

Value extends object

StoreExt extends object = object

Value

Initial store value.

PreinitializedMapStore<Value> & StoreExt

The store object with methods to subscribe.

Other overloads
function map<Value, StoreExt>(value?): PreinitializedMapStore<Partial<Value>> & StoreExt;

Create map store with no initial value, or one that may be missing.

Value extends object

StoreExt extends object = object

Value

Initial store value, if there is one.

PreinitializedMapStore<Partial<Value>> & StoreExt

The store object with methods to subscribe.

computed<Value, OriginStores extends readonly Store[]>(stores: readonly [...OriginStores], cb: (...values: StoreValues<OriginStores>) => Value | Task<Value>): ReadableAtom<Value>

Create derived store, which use generates value from another stores.

import { computed } from 'nanostores'
import { $users } from './users.js'
export const $admins = computed($users, users => {
return users.filter(user => user.isAdmin)
})

Use @nanostores/async for async function.

Other overloads

Deprecated: Use @nanostores/async.

computed<Value, OriginStore extends Store>(stores: OriginStore, cb: (value: StoreValue<OriginStore>) => Task<Value>): ReadableAtom<Value | undefined>

Deprecated: Use @nanostores/async.

computed<Value, OriginStores extends readonly Store[]>(stores: readonly [...OriginStores], cb: (...values: StoreValues<OriginStores>) => Task<Value>): ReadableAtom<Value | undefined>
computed<Value, OriginStore extends Store>(stores: OriginStore, cb: (value: StoreValue<OriginStore>) => Value): ReadableAtom<Value>
batched<Value, OriginStores extends readonly Store[]>(stores: readonly [...OriginStores], cb: (...values: StoreValues<OriginStores>) => Value | Task<Value>): ReadableAtom<Value>

Create derived store, which use generates value from another stores.

import { batched } from 'nanostores'
const $sortBy = atom('id')
const $category = atom('')
export const $link = batched([$sortBy, $category], (sortBy, category) => {
return `/api/entities?sortBy=${sortBy}&category=${category}`
})
Other overloads
batched<Value, OriginStore extends Store>(stores: OriginStore, cb: (value: StoreValue<OriginStore>) => Value | Task<Value>): ReadableAtom<Value>
effect<OriginStores extends readonly ListenableStore[]>(stores: readonly [...OriginStores], cb: (...values: StoreValues<OriginStores>) => void | (() => void)): () => void

Subscribe for multiple stores. Also you can define cleanup function to call on stores changes.

const $enabled = atom(true)
const $interval = atom(1000)
const cancelPing = effect([$enabled, $interval], (enabled, interval) => {
if (!enabled) return
const intervalId = setInterval(() => {
sendPing()
}, interval)
return () => {
clearInterval(intervalId)
}
})
Other overloads
effect<OriginStore extends ListenableStore>(stores: OriginStore, cb: (value: StoreValue<OriginStore>) => void | (() => void)): () => void
function batch(fn): void;

Run multiple store changes as a single transaction. While inside batch, listeners are deferred and each listener fires at most once when the outermost batch returns — with the final store values.

Batches can be nested; only the outermost batch triggers the flush.

Synchronous only. The batch closes when fn returns, so writes performed in microtasks, timers, or after await will not be batched. Pass a sync function.

Map#setKey writes are coalesced as well, so the listener’s changed argument is undefined for the batched notification.

import { atom, batch } from 'nanostores'
let $a = atom(0)
let $b = atom(0)
batch(() => {
$a.set(1)
$b.set(2)
})

() => void

Callback that performs the store writes.

void

get(): Value;

Get store value.

In contrast with ReadableAtom#value this value will be always initialized even if store had no listeners.

$store.get()

Value

Store value.

set(newValue): void;

Change store value.

$router.set({ path: location.pathname, page: parse(location.pathname) })

Value

New store value.

void

setKey<Key>(key, value): void;

Change key in store value.

$settings.setKey('theme', 'dark')

To delete key set undefined.

$settings.setKey('theme', undefined)

Key extends string | number | symbol

Key

The key name.

ValueWithUndefinedForIndexSignatures<Value, Key>

New value.

void

listen(listener): () => void;

Subscribe to store changes.

In contrast with Store#subscribe it do not call listener immediately.

(value, oldValue) => void

Callback with store value and old value.

Function to remove listener.

() => void

subscribe(listener): () => void;

Subscribe to store changes and call listener immediately.

import { $router } from '../store'
$router.subscribe(page => {
console.log(page)
})

(value, oldValue?) => void

Callback with store value and old value.

Function to remove listener.

() => void

function onMount<Shared>($store, initialize): () => void;

Run constructor on first store’s listener and run destructor on last listener unsubscription. It has a debounce to prevent flickering.

A way to reduce memory and CPU usage when you do not need a store.

You can communicate between listeners by payload.shared.

import { onMount } from 'nanostores'
// Listen for URL changes on first store’s listener.
onMount($router, () => {
parse()
window.addEventListener('popstate', parse)
return () => {
window.removeEventListener('popstate', parse)
}
})

Shared = never

Store

Store to listen.

(payload) => void | (() => void)

Store constructor. Returns store destructor.

A function to remove constructor and destructor from store.

() => void

const STORE_UNMOUNT_DELAY: number;
function listenKeys<SomeStore>(
$store,
keys,
listener
): () => void;

Listen for specific keys of the store.

In contrast with subscribeKeys it do not call listener immediately.

import { listenKeys } from 'nanostores'
listenKeys($page, ['blocked'], (value, oldValue, changed) => {
if (value.blocked) {
console.log('You has no access')
}
})

SomeStore extends object

SomeStore

The store to listen.

readonly MapStoreKeys<SomeStore>[]

The keys to listen.

(value, oldValue, changed) => void

Standard listener.

() => void

function subscribeKeys<SomeStore>(
$store,
keys,
listener
): () => void;

Listen for specific keys of the store and call listener immediately. Note that the oldValue and changed arguments in the listener are undefined during the initial call.

import { subscribeKeys } from 'nanostores'
subscribeKeys($page, ['blocked'], (value, oldValue, changed) => {
if (value.blocked) {
console.log('You has no access')
}
})

SomeStore extends object

SomeStore

The store to listen.

readonly MapStoreKeys<SomeStore>[]

The keys to listen.

(value, oldValue, changed) => void

Standard listener.

() => void

function setPath<T, K>(
obj,
path,
value
): T;

Set a deep value by path. Copies are made at each level of path so that no part of the original object is mutated (but it does not do a full deep copy — some sub-objects may still be shared between the old value and the new one). Sparse arrays will be created if you set arbitrary length.

import { setPath } from 'nanostores'
setPath({ a: { b: { c: [] } } }, 'a.b.c[1]', 'hey')
// Returns `{ a: { b: { c: [<empty>, 'hey'] } } }`

T extends BaseDeepMap

K extends string

T

Any object.

K

Path split by dots and []. Like: props.arr[1].nested.

FromPath<T, K>

T

The new object.

function getPath<T, K>(obj, path): FromPath<T, K>;

Get a value by object path. undefined if key is missing.

import { getPath } from 'nanostores'
getPath({ a: { b: { c: ['hey', 'Hi!'] } } }, 'a.b.c[1]') // Returns 'Hi!'

T extends BaseDeepMap

K extends string

T

Any object.

K

Path split by dots and []. Like: props.arr[1].nested.

FromPath<T, K>

The value for this path. Undefined if key is missing.

function setByKey<T>(
obj,
splittedKeys,
value
): T;

Set a deep value by key. Copies are made at each level of path so that no part of the original object is mutated (but it does not do a full deep copy — some sub-objects may still be shared between the old value and the new one). Sparse arrays will be created if you set arbitrary length.

import { setByKey } from 'nanostores'
setByKey({ a: { b: { c: [] } } }, ['a', 'b', 'c', 1], 'hey')
// Returns `{ a: { b: { c: [<empty>, 'hey'] } } }`

T extends BaseDeepMap

T

Any object.

PropertyKey[]

An array of keys representing the path to the value.

unknown

New value.

T

The new object.

function task<Return>(cb): Task<Return>;

Track store async task by wrapping promise callback. It is useful for test to wait end of the processing.

import { task } from 'nanostores'
async function saveUser () {
await task(async () => {
await api.submit('/user', user.get())
$user.setKey('saved', true)
})
}

Return = never

() => Return | Promise<Return>

Async callback with task.

Task<Return>

Return value from callback.

function startTask(): () => void;

Track store async task by start/end functions. It is useful for test to wait end of the processing.

It you use async/await in task, you can use task.

import { startTask } from 'nanostores'
function saveUser () {
const endTask = startTask()
api.submit('/user', user.get(), () => {
$user.setKey('saved', true)
endTask()
})
}

() => void

function allTasks(): Promise<void>;

Return Promise until all current tasks (and tasks created while waiting).

It is useful in tests to wait all async processes in the stores.

import { allTasks } from 'nanostores'
it('saves user', async () => {
saveUser()
await allTasks()
expect($user.get().saved).toBe(true)
})

Promise<void>

function cleanTasks(): void;

Forget all tracking tasks. Use it only for tests. cleanStores cleans tasks automatically.

import { cleanTasks } from 'nanostores'
afterEach(() => {
cleanTasks()
})

void

function keepMount($store): void;

Prevent destructor call for the store.

Together with cleanStores is useful tool for tests.

import { keepMount } from 'nanostores'
keepMount($store)

| Store | MapCreator<any, []>

The store.

void

function cleanStores(...stores): void;

Destroys all cached stores and call

It also reset all tasks by calling cleanTasks.

import { cleanStores } from 'nanostores'
afterEach(() => {
cleanStores($router, $settings)
})

…( | MapCreator<any, any[]> | Store | undefined)[]

Used store classes.

void

Promise for stores destroying.

const clean: unique symbol;
function onStart<Shared>($store, listener): () => void;

Add listener on first store listener.

We recommend to always use onMount instead to prevent flickering (onMount have small delay before unsubscribe callback). See onMount to add constructor and destructor for the store.

You can communicate between listeners by payload.shared.

Shared = never

Store

The store to add listener.

(payload) => void

Event callback.

A function to remove listener.

() => void

function onStop<Shared>($store, listener): () => void;

Add listener on last store listener unsubscription.

We recommend to always use onMount instead to prevent flickering (onMount have small delay before unsubscribe callback). See onMount to add constructor and destructor for the store.

You can communicate between listeners by payload.shared.

Shared = never

Store

The store to add listener.

(payload) => void

Event callback.

A function to remove listener.

() => void

function onSet<Shared, SomeStore>($store, listener): () => void;

Add listener to store chagings.

import { onSet } from 'nanostores'
onSet($store, ({ newValue, abort }) => {
if (!validate(newValue)) {
abort()
}
})

You can communicate between listeners by payload.shared or cancel changes by payload.abort().

New value of the all store will be payload.newValue. On MapStore#setKey() call, changed value will be in payload.changed.

Shared = never

SomeStore extends Store = Store

SomeStore

The store to add listener.

(payload) => void

Event callback.

A function to remove listener.

() => void

function onNotify<Shared, SomeStore>($store, listener): () => void;

Add listener to notifying about store changes.

You can communicate between listeners by payload.shared or cancel changes by payload.abort().

On MapStore#setKey() call, changed value will be in payload.changed.

Shared = never

SomeStore extends Store = Store

SomeStore

The store to add listener.

(payload) => void

Event callback.

A function to remove listener.

() => void

function mapCreator<Value, Args, StoreExt>(init?): MapCreator<Value, Args>;

Create function to create map stores. It will be like a class for store.

Value extends object

Args extends any[] = []

StoreExt extends object = object

(store, id, …args) => void | (() => void)

Store’s initializer. Returns store destructor.

MapCreator<Value, Args>

function readonlyType<Value>(store): ReadableAtom<Value>;

Change store type for readonly for export.

import { readonlyType } from 'nanostores'
const $storePrivate = atom(0)
export const $store = readonlyType($storePrivate)

Value

ReadableAtom<Value>

The store to be exported.

ReadableAtom<Value>

The readonly store.

Store object.

Type details

Value = any

readonly init: Value | undefined;

Low-level attribute to read store’s initial value.

readonly lc: number;

Listeners count.

readonly value: Value | undefined;

Low-level method to read store’s value without calling onStart.

Try to use only ReadableAtom#get. Without subscribers, value can be undefined.

eq(oldValue, newValue): boolean;

Compares the previous and next values on every set.

Returning true means the values are the same, so the store keeps the old value and no listener is called.

ReadonlyIfObject<Value> | undefined

ReadonlyIfObject<Value>

boolean

true if the values are equal, false otherwise.

Called only by the store’s own writes, so a store that never writes never calls it. Map#setKey uses MapStore#eqKey instead.

Before the first write, oldValue can be undefined, for instance on the first run of a computed store.

Object.is

See $store.get().

See $store.listen().

notify(oldValue?): void;

Low-level method to notify listeners about changes in the store.

Can cause unexpected behaviour when combined with frontend frameworks that perform equality checks for values, such as React.

ReadonlyIfObject<Value>

void

off(): void;

Unbind all listeners.

void

See $store.subscribe().

Store with a way to manually change the value.

Type details

Value = any

See $store.set().

Store with a way to manually change the value.

Type details

Value extends object = any

eqKey<Key>(
oldValue,
newValue,
key
): boolean;

Compares the previous and next values for a key on setKey.

Returning true means the values are the same, so the store keeps the old value and no listener is called.

One function serves every key of the map, so the key name comes as the third argument. The old value is undefined when the key is not in the map yet.

Key extends string | number | symbol

Get<Value, Key> | undefined

Get<Value, Key> | undefined

Key

boolean

true if the values are equal, false otherwise.

Deleting an existing key skips the comparison and notifies either way. Whole-value writes use ReadableAtom#eq instead.

Object.is
listen(listener): () => void;

Subscribe to store changes.

In contrast with Store#subscribe it do not call listener immediately.

(value, oldValue, changedKey) => void

Callback with store value and old value.

Function to remove listener.

() => void

WritableAtom.listen

notify(oldValue?, changedKey?): void;

Low-level method to notify listeners about changes in the store.

Can cause unexpected behaviour when combined with frontend frameworks that perform equality checks for values, such as React.

ReadonlyIfObject<Value>

AllKeys<Value>

void

WritableAtom.notify

set(newValue): void;

Change store value.

$settings.set({ theme: 'dark' })

Operation is atomic, subscribers will be notified once with the new value. changedKey will be undefined

Value

New store value.

void

WritableAtom.set

See $store.setKey().

subscribe(listener): () => void;

Subscribe to store changes and call listener immediately.

import { $router } from '../store'
$router.subscribe(page => {
console.log(page)
})

(value, oldValue, changedKey) => void

Callback with store value and old value.

Function to remove listener.

() => void

WritableAtom.subscribe

Type details
  • Promise<Value>

Value

readonly [toStringTag]: string;
Promise.[toStringTag]
t: true;
catch<TResult>(onrejected?): Promise<Value | TResult>;

Attaches a callback for only the rejection of the Promise.

TResult = never

((reason) => TResult | PromiseLike<TResult>) | null

The callback to execute when the Promise is rejected.

Promise<Value | TResult>

A Promise for the completion of the callback.

Promise.catch
finally(onfinally?): Promise<Value>;

Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The resolved value cannot be modified from the callback.

(() => void) | null

The callback to execute when the Promise is settled (fulfilled or rejected).

Promise<Value>

A Promise for the completion of the callback.

Promise.finally
then<TResult1, TResult2>(onfulfilled?, onrejected?): Promise<TResult1 | TResult2>;

Attaches callbacks for the resolution and/or rejection of the Promise.

TResult1 = Value

TResult2 = never

((value) => TResult1 | PromiseLike<TResult1>) | null

The callback to execute when the Promise is resolved.

((reason) => TResult2 | PromiseLike<TResult2>) | null

The callback to execute when the Promise is rejected.

Promise<TResult1 | TResult2>

A Promise for the completion of which ever callback is executed.

Promise.then

Store with a way to manually change the value.

Type details

Value

readonly value: Value;

Low-level method to read store’s value without calling onStart.

Try to use only ReadableAtom#get. Without subscribers, value can be undefined.

WritableAtom.value

Store with a way to manually change the value.

Type details

Value extends object = any

readonly value: Value;

Low-level method to read store’s value without calling onStart.

Try to use only ReadableAtom#get. Without subscribers, value can be undefined.

MapStore.value

Type details

Value extends object = any

Args extends any[] = []

MapCreator(id, ...args): MapStore<object & Value>;

string

…Args

MapStore<object & Value>

cache: object;
[id: string]: MapStore<object & Value>
build(id, ...args): MapStore<object & Value>;

string

…Args

MapStore<object & Value>

type Store<Value> =
| ReadableAtom<Value>
| WritableStore<Value>;
Type details

Value = any

type WritableStore<Value> =
| Value extends object ? MapStore<Value> : never
| WritableAtom<Value>;
Type details

Value = any

type AnyStore<Value> = Gettable<Value> & object;
Type details

Value = any

readonly value: undefined | Value;
type Atom<Value> =
| ReadableAtom<Value>
| WritableAtom<Value>;
Type details

Value = any

type ListenableStore<Value> = Gettable<Value> & object;
Type details

Value = any

listen(listener): () => void;

() => void

() => void

type Gettable<Value> = object;
Type details

Value = any

get(): Value;

Value

type StoreValue<SomeStore> = SomeStore extends Gettable<infer Value> ? Value : any;
Type details

SomeStore

type StoreValues<Stores> = { -readonly [Index in keyof Stores]: StoreValue<Stores[Index]> };
Type details

Stores extends readonly Gettable[]

type MapStoreKeys<SomeStore> = SomeStore extends MapStore ? AllKeys<StoreValue<SomeStore>> : SomeStore extends object ? K : AllKeys<StoreValue<SomeStore>>;
Type details

SomeStore

type DeepMapStore<T> = object & Omit<WritableAtom<T>, "listen" | "notify" | "setKey" | "subscribe">;
Type details

T extends BaseDeepMap

setKey: <K>(key, value) => void;

Change key in store value. Copies are made at each level of key so that no part of the original object is mutated (but it does not do a full deep copy — some sub-objects may still be shared between the old value and the new one).

$settings.setKey('visuals.theme', 'dark')

K extends AllPaths<T>

K

The key name. Attributes can be split with a dot . and [].

FromPathWithIndexSignatureUndefined<T, K>

New value.

void

listen(listener): () => void;

Subscribe to store changes.

In contrast with Store#subscribe it do not call listener immediately.

(value, oldValue, changedKey) => void

Callback with store value and old value.

Function to remove listener.

() => void

notify(oldValue?, changedKey?): void;

Low-level method to notify listeners about changes in the store.

Can cause unexpected behaviour when combined with frontend frameworks doing equality checks for values, e.g. React.

T

AllPaths<T>

void

subscribe(listener): () => void;

Subscribe to store changes and call listener immediately.

import { $settings } from '../store'
$settings.subscribe(settings => {
console.log(settings)
})

(value, oldValue, changedKey) => void

Callback with store value and old value.

Function to remove listener.

() => void

type BaseDeepMap = Record<string, unknown>;
type AllPaths<T, P, MaxDepth> = T extends infer U[] ?
| `${P}[${number}]`
| AllPaths<U, `${P}[${number}]`, Subtract<MaxDepth, 1>>
| P : T extends BaseDeepMap ? MaxDepth extends 0 ? never : { [K in keyof T]-?: K extends number | string ? AllPaths<T[K], ConcatPath<P, `${K}`>, Subtract<MaxDepth, 1>> | (P extends "" ? never : P) : never }[keyof T] : P;
Type details

T

P extends string = ""

MaxDepth extends number = 10

type FromPath<T, P> = T extends unknown ? NestedArrKey<T, P> extends never ? NestedObjKey<T, P> extends never ? P extends keyof T ? T[P] : never : NestedObjKey<T, P> : NestedArrKey<T, P> : never;
Type details

T

P

function deepMap<T>(init?): DeepMapStore<T>;

Create deep map store. Deep map store is a store with an object as store value, that supports fine-grained reactivity for deeply nested properties.

T extends BaseDeepMap

T

Initialize store and return store destructor.

DeepMapStore<T>

The store object with methods to subscribe.

function getKey<T, K>(store, key): FromPath<T, K>;

T extends Record<string, unknown>

K extends string

AnyStore<T>

K

FromPath<T, K>