Nano Stores
Nano Stores is a lightweight and performant state manager designed for a wide range of JavaScript frameworks including React, Preact, Vue, Svelte, and vanilla JS. Currently stable at version 1.3.0, the library maintains an active release cadence, frequently introducing new features and improvements. Its core philosophy revolves around using many atomic, tree-shakable stores and direct manipulation, which contributes to its incredibly small footprint (294-831 bytes minified and brotlied) and zero external dependencies. Key differentiators include its focus on moving business logic out of components into dedicated stores, optimizing for speed by avoiding unnecessary selector calls, and providing first-class TypeScript support. This architecture ensures excellent tree-shaking, only bundling the stores actually used in a given chunk.
Common errors
-
ERR_REQUIRE_ESM ... require() of ES Module ... Not supported
cause Attempting to use `require()` to import Nano Stores, which is distributed as an ES Module (ESM).fixUse ES Module `import` syntax (`import { atom } from 'nanostores'`). Ensure your project is configured for ESM (e.g., by setting `"type": "module"` in `package.json` or using a bundler like Webpack/Rollup/Vite). -
TypeError: $store.subscribe is not a function
cause This error often indicates that you are trying to call a store method (like `.subscribe()`, `.get()`, or `.set()`) on a variable that is not a valid Nano Stores store object, or you've incorrectly imported a framework-specific utility instead of a core store.fixVerify that the variable `$store` is indeed a store created by `atom()` or `computed()`. Also, ensure you are importing core store functions (e.g., `atom`, `computed`) from `nanostores` and framework-specific utilities (e.g., `useStore`) from their respective packages (e.g., `@nanostores/react`). -
Error: Node.js v18.x is no longer supported.
cause You are running a Nano Stores version 1.0.0 or higher on an unsupported Node.js v18.x environment.fixUpgrade your Node.js version to `20.0.0` or `22.0.0` (or newer) to meet the minimum engine requirements for Nano Stores v1.0.0 and above.
Warnings
- breaking Node.js v18.x support was removed in Nano Stores v1.0.0. Projects using older Node.js versions will encounter runtime errors or installation failures.
- deprecated The async `computed` functionality within the main `nanostores` package was deprecated in favor of a separate `@nanostores/async` package.
- deprecated The `deepmap()` utility was deprecated in favor of a separate `@nanostores/deepmap` package.
Install
-
npm install nanostores -
yarn add nanostores -
pnpm add nanostores
Imports
- atom
const { atom } = require('nanostores')import { atom } from 'nanostores' - computed
import { computed as asyncComputed } from 'nanostores'import { computed } from 'nanostores' - useStore
import { useStore } from 'nanostores'import { useStore } from '@nanostores/react'
Quickstart
interface Task {
id: string;
text: string;
completed: boolean;
}
import { atom, computed } from 'nanostores';
// 1. Create an atomic store using `atom`
export const $tasks = atom<Task[]>([]);
// 2. Define actions to manipulate the store
export function addTask(text: string) {
const newTask: Task = {
id: String(Date.now()),
text,
completed: false
};
$tasks.set([...$tasks.get(), newTask]);
console.log(`Added task: "${text}"`);
}
export function toggleTask(id: string) {
$tasks.set($tasks.get().map(task =>
task.id === id ? { ...task, completed: !task.completed } : task
));
console.log(`Toggled task with ID: ${id}`);
}
// 3. Create a derived store using `computed`
export const $pendingTasksCount = computed($tasks, tasks =>
tasks.filter(task => !task.completed).length
);
// 4. Subscribe to store changes
console.log('--- Initial State ---');
console.log('Tasks:', $tasks.get());
console.log('Pending count:', $pendingTasksCount.get());
const unsubscribeTasks = $tasks.listen(currentTasks => {
console.log('\n--- Tasks Updated ---');
console.log('Current tasks:', currentTasks.map(t => `${t.text} (${t.completed ? 'Done' : 'Pending'})`).join(', '));
});
const unsubscribePendingCount = $pendingTasksCount.listen(count => {
console.log('Current pending tasks count:', count);
});
// 5. Trigger actions to see state changes
addTask('Learn Nano Stores');
addTask('Build a cool app');
toggleTask($tasks.get()[0]?.id || '');
setTimeout(() => {
addTask('Review documentation');
toggleTask($tasks.get()[1]?.id || '');
console.log('\n--- Final State after Timeout ---');
console.log('Tasks:', $tasks.get().map(t => `${t.text} (${t.completed ? 'Done' : 'Pending'})`).join(', '));
console.log('Pending count:', $pendingTasksCount.get());
// Clean up subscriptions (important in real apps)
unsubscribeTasks();
unsubscribePendingCount();
}, 200);