Skip to main content
The global store gives every model on your page access to the same key/value state without requiring you to wire models together manually. It is useful whenever two or more models need to react to the same piece of data — a selected theme, a logged-in user, a notification count — without one model knowing about the other’s implementation.

How it works

Sprincul.store is a flat key/value map backed by nanostores atoms. Any model (or any script on the page) can read from it, write to it, and subscribe to changes. You do not need to configure it; it is available as soon as you import Sprincul.

API reference

Sprincul.store.set(key, value)

Writes value under key. If the key does not exist yet, Sprincul creates the underlying atom automatically. All active subscribers for that key are notified.
Sprincul.store.set('theme', 'dark');

Sprincul.store.get(key)

Returns the current value stored under key, or undefined if nothing has been set yet.
const theme = Sprincul.store.get('theme'); // 'dark' | undefined

Sprincul.store.subscribe(key, callback)

Registers callback to run whenever the value at key changes. Returns an unsubscribe function you can call to stop listening.
const unsubscribe = Sprincul.store.subscribe('theme', (value) => {
  console.log('theme changed to:', value);
});

// Later, when you no longer need updates:
unsubscribe();
subscribe fires after a value changes, not when you first call it. To read the current value at subscription time, call Sprincul.store.get(key) before subscribing.

Sprincul.store.clear()

Removes all entries from the store. Existing subscribers are not automatically cleaned up, so call unsubscribe() on any active subscriptions before clearing if you want to avoid stale callbacks.
Sprincul.store.clear();

When to seed store values

You can write to the store at any point — before registering models, before calling Sprincul.init(), or after. There is no required ordering. If you want a default value in place before any model reads or subscribes, set it early:
// Seed a default before init
Sprincul.store.set('theme', 'light');

Sprincul.register('ThemeSwitcher', ThemeSwitcher);
Sprincul.register('HeroSection', HeroSection);
Sprincul.init();

Practical example: theme switcher and themed component

The following example shows two independent models communicating through the store. ThemeSwitcher writes the active theme, and HeroSection reads and reacts to it.
<style>[data-cloaked] { display: none }</style>

<div data-model="ThemeSwitcher">
  <button onclick="setLight">Light</button>
  <button onclick="setDark">Dark</button>
</div>

<section data-model="HeroSection" data-cloaked>
  <h1 data-bind-theme="applyTheme">Welcome</h1>
</section>
Reading the store in beforeInit() and subscribing in afterInit() is the recommended pattern. beforeInit() runs before bindings are attached, so any state you set there is ready when the first render pass fires.

Avoiding memory leaks

Every call to subscribe returns an unsubscribe function. Hold onto it and call it when the model is torn down or when you no longer need the subscription. If you subscribe inside afterInit(), a good pattern is to store the unsubscribe function on the instance:
import { SprinculModel, Sprincul } from 'sprincul';

export default class Sidebar extends SprinculModel {
  #unsubscribeUser = null;

  afterInit() {
    this.#unsubscribeUser = Sprincul.store.subscribe('currentUser', (user) => {
      this.state.username = user?.name ?? 'Guest';
    });
  }

  // Call this when you remove the model from the DOM, or let Sprincul's
  // built-in cleanup handle it if the element is removed naturally.
  teardown() {
    this.#unsubscribeUser?.();
  }
}
Sprincul automatically destroys model instances when their root element is removed from the DOM, so subscriptions tied to that model’s lifetime are cleaned up as part of normal page lifecycle management.