# Framework bindings
Source: https://selfstore.dev/docs/frameworks
React, Svelte and Vue in a few lines each - there is deliberately no adapter package to install.
The whole binding contract is two members every store has: `store.subscribe(fn)`
and `store.state` (with `store.status` and `store.error` on top). The state
snapshot is **referentially stable** between changes, so equality-based
frameworks re-render only when something actually changed. That is why there is
no `selfstore-react` package: it would be three lines in a trench coat.
## React
```tsx
import { useSyncExternalStore } from 'react';
function useStatus() {
useSyncExternalStore(store.subscribe, () => store.state, () => store.state);
return store.status; // { state, severity, action, labelKey }
}
function SaveBadge() {
const status = useStatus();
return {t(status.labelKey)};
}
```
The **third argument matters**: it is the server snapshot, and without it
`useSyncExternalStore` throws during SSR (Next.js, Remix). Keep it.
## Svelte
The store contract is structural, so this object **is** a Svelte readable, no
`svelte` import needed, Svelte 3 through 5:
```ts
export const persistence = {
subscribe(run: (s: typeof store.state) => void) {
run(store.state);
return store.subscribe(() => run(store.state));
},
};
```
```svelte
{t($persistence.status.labelKey)}
```
## Vue
```ts
import { onMounted, onUnmounted, shallowRef } from 'vue';
export function usePersistence() {
const state = shallowRef(store.state);
let stop: (() => void) | undefined;
onMounted(() => (stop = store.subscribe(() => (state.value = store.state))));
onUnmounted(() => stop?.());
return state;
}
```
Solid, Preact signals, Lit, vanilla: same shape. Subscribe on mount, read
`store.state`, call the returned unsubscriber on unmount.
## Two gestures, and teardown
`store.status.action` is set only when the user must act, and only to one of
two values. Offer exactly that gesture:
```ts
if (store.status.action === 'unlock') await store.unlock(await promptPassword());
if (store.status.action === 'reconnect') await store.reconnect();
```
Transient trouble (offline, a cold-started backend, a 5xx) never sets `action`:
the edit stays safe locally and the next save or sync retries on its own.
In an SPA, call `store.dispose()` when a route-scoped or test-scoped store
unmounts: it cancels timers and drops subscribers. An app-wide store needs
nothing.
Map of this site for a model: https://selfstore.dev/llms.txt
Every page in one file: https://selfstore.dev/llms-full.txt