Skip to content

Introduction

Nano Kit is a lightweight, modular, and performant state management ecosystem for building modern web applications. Each library is designed to be small and tree-shakeable, while still offering rich DX and useful features.

import { signal, mountable, onMount, computed, effect } from '@nano_kit/store'
/* Create independent mountable atomic store */
const $count = mountable(signal(1))
/* Mountable: Run logic only when store has listeners */
onMount($count, () => {
console.log('Mounted: Store is active')
/* e.g., open websocket, start timer, etc. */
return () => {
console.log('Unmounted: Store is idle')
/* e.g., close websocket, stop timer */
}
})
/* Derive state (computed values are lazy & cached) */
const $double = computed(() => $count() * 2)
/* React to changes (triggers onMount) */
const unsub = effect(() => {
console.log(`Count: ${$count()}, Double: ${$double()}`)
})
/* Update triggers granular propagation */
$count(2)
/* Cleanup: removes listener and triggers onMount destructor */
unsub()
PackageDescription
@nano_kit/storeSignals-based state management library.
@nano_kit/queryData fetching and caching library, built on @nano_kit/store.
@nano_kit/routerRouting library, built on @nano_kit/store.
@nano_kit/ssrBase package for building framework-specific SSR adapters.
AdapterDescription
@nano_kit/reactReact integration for @nano_kit/store.
@nano_kit/react-routerReact integration for @nano_kit/router.
@nano_kit/react-ssrReact SSR adapter for @nano_kit/ssr.
@nano_kit/preactPreact integration for @nano_kit/store.
@nano_kit/preact-routerPreact integration for @nano_kit/router.
@nano_kit/preact-ssrPreact SSR adapter for @nano_kit/ssr.
@nano_kit/svelteSvelte integration for @nano_kit/store.
@nano_kit/svelte-routerSvelte integration for @nano_kit/router.
@nano_kit/svelte-ssrSvelte SSR adapter for @nano_kit/ssr.
@nano_kit/svelte-kitSvelteKit integration for stores, router, and SSR.
@nano_kit/next-routerNext.js integration for @nano_kit/router.

Nano Kit is built on the philosophy of Nano Stores and extends its core ideas:

  • Atomicity: Use independent, atomic stores for granular updates.
  • Mountable stores: Initialize resources only when the store is in use (on first listener) and clean up automatically.
  • Shift logic out of components: Move business logic to the store level.
  • Minimal footprint: Maintain a tiny core size that is fully tree-shakeable.

However, Nano Stores has some limitations:

  • Performance overhead
  • Suboptimal Developer Experience (DX)
  • Issues with SSR support

Nano Kit aims to fix these issues while keeping the same principles.

Adopting an ecosystem-first architecture provides a much smoother experience for developers than a stack of independent libraries. Because every part of the ecosystem is built on the same foundation, you get a consistent “feel” across the entire library. This shared core allows the tools to reuse internal logic, which drastically reduces your final bundle size.

Compare this to a standard setup like React Router + Redux + React Query. While these are great tools, they are effectively strangers to each other. They each have their own way of doing things and their own internal weight, often leading to duplicated code. Consequently, developers are forced to manually synchronize state and data between these independent tools inside the UI components, often resulting in bloated “glue code”.

Nano Kit takes a different approach. Because the router, data fetching, and state management layer are engineered to work together from day one, they integrate deeply at the store level. This removes the need for messy “glue code” in your components and keeps your application logic clean, unified, and efficient. It makes the store layer portable and independent, allowing you to easily swap the UI layer.

import { browserNavigation, searchParams, searchParam } from '@nano_kit/router'
import { client, queryKey } from '@nano_kit/query'
import { effect } from '@nano_kit/store'
import { useSignal } from '@nano_kit/react'
/* Router */
const [$location, navigation] = browserNavigation({ /* routes */ })
const $searchParams = searchParams($location)
const $page = searchParam($searchParams, 'page', v => (v ? Number(v) : 1))
/* Query */
const { query } = client()
const [
$episodes,
$episodesError,
$episodesLoading
] = query(queryKey('episodes'), [$page], async (page) => {
/* Fetcher automatically receives the current page */
return fetch(`/api/episodes?page=${page}`).then(r => r.json())
})
/* Logic is fully decoupled from UI components */
effect(() => {
console.log('Current page:', $page())
console.log('Episodes:', $episodes())
})
/* UI components remain pure and logic-free. Example (React): */
function MyComponent() {
const page = useSignal($page)
const episodes = useSignal($episodes)
/* ... */
}
/* Changing the URL updates $page signal and automatically triggers re-fetch */
navigation.push('/episodes?page=2')

To achieve high performance, Nano Kit uses the push-pull algorithm from alien-signals as the core of its reactivity system. A dedicated fork named “Agera” was created to support additional required features.

The benchmark results below show how @nano_kit/store (Agera) compares to other popular state management libraries.

LibraryLatency avg (ns)Latency med (ns)Throughput avg (ops/s)Throughput med (ops/s)Samples
alien-signals308.59
± 1.78%
280.00
± 0.00
3441332
± 0.01%
3571429
± 0
3240532
@nano_kit/store320.68
± 1.94%
290.00
± 10.00
3309252
± 0.01%
3448276
± 107527
3118402
@preact/signals-core338.61
± 1.72%
310.00
± 0.00
3199314
± 0.01%
3225806
± 0
2953273
svelte/store465.27
± 1.90%
390.00
± 10.00
2407386
± 0.02%
2564103
± 67476
2149277
rxjs494.07
± 1.64%
440.00
± 10.00
2171288
± 0.02%
2272727
± 52854
2023990
zustand, subscribeWithSelector949.00
± 11.36%
540.00
± 10.00
1759331
± 0.03%
1851852
± 34941
1061940
nanostores1301.5
± 3.82%
1010.0
± 30.00
922683
± 0.04%
990099
± 26959
768344
mobx3523.9
± 1.97%
3170.0
± 40.00
308802
± 0.03%
315457
± 3858
283777
valtio8159.3
± 42.88%
4160.0
± 270.00
224079
± 0.10%
240385
± 16186
136709
jotai12324
± 18.25%
6780.0
± 260.00
140186
± 0.11%
147493
± 5760
81699
effector26075
± 12.65%
15950
± 310.00
60217
± 0.16%
62696
± 1495
38808
@reatom/core76343
± 18.57%
50400
± 7410.0
19224
± 0.38%
19841
± 2952
13108

Median of 11 rounds, each library measured in a fresh Node.js process in shuffled order. Run on AMD Ryzen 5 PRO 3400G with Node.js v24.14.1.

Comparison of bundle sizes for various implementations of the weather example:

StackRaw SizeGzipped Size
React + Nano Stores206.77 kB65.90 kB
React + Nano Kit211.32 kB67.25 kB
React + Nano Kit + DI212.49 kB67.60 kB
React + Reatom228.16 kB73.00 kB
React + TanStack Query232.63 kB72.42 kB

Bundled with Vite 8

Comparison of frontend bundle sizes for various SSR implementations of the rick-and-morty example:

StackRaw SizeGzipped SizeRaw Size
/characters page
React + Nano Kit SSR236.62 kB78.01 kB223.87 kB
React + TanStack Start374.95 kB117.43 kB364.59 kB
Next.js Pages Router + Nano Kit509.29 kB161.25 kB367.60 kB
Next.js App Router + Nano Kit946.14 kB292.69 kB470.21 kB

Bundled with Vite 8 / Next.js 16