Pulse · Alpine-style

Global stores

One source of truth for the whole page: $.store('cart', { items: [] }). Any component that reads it updates when it changes.

Create a store with an initial object; read it anywhere with $.store('cart'), in expressions or in JavaScript. Getters work as computed values. Mutating the store re-renders every component on the page.

Persistence is a few lines: load the initial value from localStorage, and save on pagehide. The cart below survives a refresh.

Live · Shop, badge and mini-cart sharing one store

Rack Shop

Mini cart

    View the source for this demo
    HTML
    <div class="row" style="justify-content:space-between">
      <strong>Rack Shop</strong>
      <span kj-data="{}" class="chip" aria-live="polite" kj-text="'🛒 ' + $.store('cart').count + ' items · $' + $.store('cart').total"></span>
    </div>
    <div kj-data="shop" class="gs-grid">
      <button type="button" @click="add('Patch cable', 9)">Patch cable · $9</button>
      <button type="button" @click="add('Easing mug', 18)">Easing mug · $18</button>
      <button type="button" @click="add('Tween tee', 24)">Tween tee · $24</button>
    </div>
    <div kj-data="minicart" class="plate" style="box-shadow:none;margin-top:14px">
      <h4 style="margin:0 0 6px">Mini cart</h4>
      <ul class="list-plain" kj-html="list()"></ul>
      <button type="button" @click="$.store('cart').items = []" :disabled="!$.store('cart').count" style="margin-top:10px">Empty cart</button>
    </div>
    JavaScript
    let saved = [];
    try { saved = JSON.parse(localStorage.getItem('kj-demo-cart')) || []; } catch (e) {}
    
    $.store('cart', {
      items: saved,
      get count() { return this.items.length; },
      get total() { return this.items.reduce((sum, i) => sum + i.price, 0); },
    });
    
    $.component('shop', () => ({
      add(name, price) { $.store('cart').items.push({ name, price }); },
    }));
    
    $.component('minicart', () => ({
      remove(i) { $.store('cart').items.splice(i, 1); },
      list() {
        const items = $.store('cart').items;
        if (!items.length) return '<li class="muted">Your cart is empty.</li>';
        return items.map((it, i) => '<li class="gs-line">' + $.escape(it.name) + ' <span>$' + it.price +
          ' <button type="button" @click="remove(' + i + ')" aria-label="Remove ' + $.escape(it.name) + '">✕</button></span></li>').join('');
      },
    }));
    
    $(window).on('pagehide', () => {
      try { localStorage.setItem('kj-demo-cart', JSON.stringify($.store('cart').items)); } catch (e) {}
    });

    More examples

    Use a store from plain JavaScript

    JavaScript
    const prefs = $.store('prefs', { theme: 'dark', compact: false });
    $('#compact').on('change', function () { prefs.compact = this.checked; });   // every component updates

    API

    $.store(name [, initial]) → reactive object

    Create or retrieve a global reactive store. Any component reading it re-renders when it changes. Getters work as computed values.

    Reference: $.store() →
    $.reactive(object) → proxy

    A standalone reactive proxy without auto-rendering — for your own change-tracking logic.

    Reference: $.reactive() →

    Migrating

    Coming from Alpine.jsAlpine.store('cart', …) → $.store('cart', …); read it as $.store('cart') instead of $store.cart.

    Related features