Skip to content

createStorageStore

Creates a reactive nanostore bound to a single storage key.

Signature

function createStorageStore(
adapter: StorageAdapter | StorageAdapter[],
key: string,
options?: StorageStoreOptions,
): StorageStore;

Parameters

adapter

The storage adapter(s) to use.

TypeDescription
StorageAdapterSingle adapter
StorageAdapter[]Array of adapters for fallback chain
// Single adapter
createStorageStore(localStorageAdapter, "key");
// Fallback chain
createStorageStore([sessionStorageAdapter, localStorageAdapter], "key");

key

The storage key to bind to.

TypeDescription
stringStorage key name

options

Optional configuration object.

interface StorageStoreOptions {
listen?: boolean;
defaultValue?: string | null;
}
OptionTypeDefaultDescription
listenbooleanfalseEnable cross-tab sync
defaultValuestring | nullnullInitial fallback value when storage is empty

Return Value

Returns a StorageStore object:

interface StorageStore {
readonly $value: ReadableAtom<string | null>;
get: () => string | null;
set: (value: string) => void;
remove: () => void;
sync: () => void;
readonly listener: StorageListener;
}

$value

A nanostores ReadableAtom containing the current value. Use get() and set() methods to modify.

// Subscribe to changes
const unsubscribe = store.$value.subscribe((value) => {
console.log(value);
});
// Listen (skip initial call)
store.$value.listen((value) => {
console.log("Changed to:", value);
});

get()

Returns the current value.

const value = store.get(); // string | null

set(value)

Sets a new value. Writes to all adapters in a fallback chain.

store.set("new value");

remove()

Removes the value from storage and resets $value to null.

store.remove();
// localStorage.getItem("key") === null
// store.get() === null

sync()

Force sync from storage to store. Useful when:

  • listen: false and you want to manually refresh
  • Polling for same-tab changes from other libraries (with setInterval)
  • Using adapters without event support (e.g., cookies)
// Manual refresh when listen: false
store.sync();
// Polling pattern for same-tab changes (not recommended for most cases)
const interval = setInterval(() => store.sync(), 1000);

listener

Controls cross-tab synchronization.

interface StorageListener {
on: () => void;
off: () => void;
toggle: () => void;
readonly $on: ReadableAtom<boolean>;
}
MemberDescription
on()Begin listening for external changes
off()Stop listening
toggle()Toggle listening state
$onReactive boolean indicating listener status

Examples

Basic Usage

import { createStorageStore, localStorageAdapter } from "@vp-tw/nanostores-storage";
const usernameStore = createStorageStore(localStorageAdapter, "username");
// Read
console.log(usernameStore.get()); // null or stored value
// Write
usernameStore.set("Alice");
// Remove
usernameStore.remove();

With Default Value (Initial Only)

const themeStore = createStorageStore(localStorageAdapter, "theme", {
defaultValue: "system",
});
// On first load (storage empty): "system"
console.log(themeStore.get());
// After remove() or if deleted from another tab: null (not "system")
themeStore.remove();
console.log(themeStore.get()); // null

With Always-On Fallback (Using computed)

import { computed } from "nanostores";
const themeStore = createStorageStore(localStorageAdapter, "theme");
// Always returns a value, never null
const $theme = computed(themeStore.$value, (v) => v ?? "system");
console.log($theme.get()); // "system" if null, otherwise stored value

With Cross-Tab Sync

const settingsStore = createStorageStore(localStorageAdapter, "settings", {
listen: true,
defaultValue: "{}",
});
// Automatically updates when changed in other tabs
settingsStore.$value.subscribe((value) => {
console.log("Settings updated:", value);
});

With Fallback Chain

const sessionStore = createStorageStore(
[sessionStorageAdapter, localStorageAdapter],
"session-data",
{ listen: true },
);

Manual Listener Control

const store = createStorageStore(localStorageAdapter, "key");
// Start when needed
store.listener.on();
console.log(store.listener.$on.get()); // true
// Stop when done
store.listener.off();

React Integration

import { useStore } from "@nanostores/react";
import { createStorageStore, localStorageAdapter } from "@vp-tw/nanostores-storage";
const counterStore = createStorageStore(localStorageAdapter, "counter", {
defaultValue: "0",
});
function Counter() {
const count = useStore(counterStore.$value);
const value = parseInt(count ?? "0", 10);
return <button onClick={() => counterStore.set(String(value + 1))}>Count: {value}</button>;
}

See Also