Nano Stores

1.3.0 · active · verified Sun Apr 19

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

Warnings

Install

Imports

Quickstart

This example demonstrates creating an atomic store with `atom`, defining actions to modify it, deriving state with `computed`, and subscribing to changes using `listen` to observe state updates.

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);

view raw JSON →