Alpine.js integration for Nano Stores, a tiny state manager with many atomic tree-shakable stores.
- Small. Less than 1.2 KB (minified + brotli). Zero dependencies.
- Tree-shakable. Use only the parts you need: directive, magic, or component helper.
- Smart. Store subscriptions are created and cleaned up automatically.
- It has good TypeScript support.
<div x-data x-nano:user="$profile">
<header>Hi, <strong x-text="user.name"></strong>!</header>
</div>npm install nanostores alpinejs @nanostores/alpineimport Alpine from 'alpinejs'
import { NanoStores } from '@nanostores/alpine'
Alpine.plugin(NanoStores)
Alpine.start()Or register individual parts:
import { directivePlugin, magicPlugin } from '@nanostores/alpine'
// Registers x-nano and x-nano-model directives
Alpine.plugin(directivePlugin)
// Registers $nano magic property
Alpine.plugin(magicPlugin)The primary way to use stores in templates. Binds a store to a named scope property on the element — reactive and available to all child expressions.
<div x-data x-nano:count="$counter">
<span x-text="count"></span>
</div>Multiple stores on one element:
<div x-data x-nano:user="$user" x-nano:cart="$cart">
<span x-text="user.name"></span>
<span x-text="cart.length + ' items'"></span>
</div>Combine reads with mutations:
<div x-data x-nano:count="$counter">
<span x-text="count"></span>
<button @click="$counter.set(count + 1)">+</button>
</div>Two-way binding between a store and a form input. Changes to the input update the store. Store changes reflect in the input.
<div x-data x-nano-model:name="$name">
<input x-model="name" />
<p x-text="'Hello, ' + name"></p>
</div>Works with
atomstores. Formapstores usex-nano+setKey.
Reads a store value inline — in any expression, on any element, without
wrapping in x-data. Useful for attribute bindings and one-off reads.
<span x-text="$nano($counter)"></span>
<span x-text="$nano($user).name"></span>
<span x-show="$nano($cart).length > 0">Cart is not empty</span>
<button :disabled="$nano($isLoading)">Save</button>Not recommended inside
x-for—x-fordoes not reliably clean up subscriptions per item. Usex-nanoon the parent element instead.
Injects store values as typed initial state and keeps them in sync through the component lifecycle. Best choice when you want to keep store logic out of the template entirely.
import Alpine from 'alpinejs'
import { withStores } from '@nanostores/alpine/with-stores'
import { $cart, $profile } from './stores.js'
Alpine.data(
'header',
withStores({ cart: $cart, profile: $profile }, ({ cart, profile }) => ({
get cartCount() {
return this.cart.length
},
get greeting() {
return `Hi, ${this.profile.name}`
},
clearCart() {
$cart.set([])
}
}))
)<header x-data="header">
<span x-text="greeting"></span>
<span x-text="cartCount + ' items'"></span>
<button @click="clearCart">Clear</button>
</header>x-nano-modelwith map stores: Onlyatomstores are supported for two-way binding. Usex-nano+setKeyfor map stores.- SSR: Alpine.js does not support server-side rendering; neither does this package.