Overview

createGlobalStore(StoreClass) instantiates a named class once on the window registry (keyed by constructor). Call it in the App root. The same class returns the existing instance — shell stores survive SPA navigation.

getGlobalStore(StoreClass) looks up that instance from children. Throws if App never called createGlobalStore for the class.

SSR: serializeLiveGlobalStores() builds a class-name → field-envelope map; buildGlobalStoreStateScript emits the transfer <script>. On hydrate, snapshots apply and load() is skipped; otherwise load() runs when present.

vs pageStore / store()

API
Lifetime
Prefer when
createGlobalStore app / shell Survives navigation; create in App, getGlobalStore in children.
getPageStore page Cleaned up when the creating component unmounts.
store(obj) module object Proxy bag without class/SSR envelopes.

createGlobalStore + getGlobalStore

App creates the store; Display looks it up. Same instance, no props.

import { html, atom, x, createGlobalStore, getGlobalStore, renderApp } from 'mates';

class CounterStore {
count = atom(0);
incr() { this.count.set(n => n + 1); }
reset() { this.count.set(0); }
}

const Display = () => {
const c = getGlobalStore(CounterStore);
return () => html`
<div class="card card-body p-10 center">
<h1>${c.count()}</h1>
</div>
`;
};

const App = () => {
const c = createGlobalStore(CounterStore);
return () => html`
<div class="m-col m-gap">
${x(Display)}
<div class="m-flex m-items-center m-justify-center m-gap-sm">
<button class="btn-primary" @click=${() => c.incr()}>+1</button>
<button class="btn-ghost" @click=${() => c.reset()}>Reset</button>
</div>
</div>
`;
};
renderApp(App, document.getElementById('app'));

Optional load()

When no SSR snapshot is present, load() runs once on create (playground has no snapshot).

import { html, atom, createGlobalStore, renderApp } from 'mates';

class PrefsStore {
label = atom('…');
load() {
this.label.set('loaded');
}
}

const App = () => {
const prefs = createGlobalStore(PrefsStore);
return () => html`
<div class="m-col m-gap">
<p class="muted">${prefs.label()}</p>
<p class="muted m-t-5">load() runs when no SSR snapshot</p>
<button class="btn-primary" @click=${() => prefs.label.set('edited')}>edit</button>
</div>
`;
};
renderApp(App, document.getElementById('app'));