# store

> `store` declares a headless reactive unit — the same members a component has, minus `render` — meant to hold UI state and actions shared across components with no backend identity. Its scope word is a closed set of exactly two keywords: `app` (one instance for the running app) or `session` (persisted across reloads, cleared on logout). ⚠ MEASURED 2026-09-10: the DECLARATION compiles, but nothing today lets a component READ a store's state or INVOKE its actions — a store's name is not in scope anywhere outside its own body. Read this page before reaching for `store` as a working reuse mechanism; it does not yet do what its name promises.

<!-- id: ui-store · area: ui · stability: preview · html: https://osysharp.com/reference/ui/store/ -->

## Summary        {#summary}
A `store` is a **component minus `render`** — the same reactive members (`state`/`derived`/`[live] data`/
`effect`/`action`/a plain `method`), sharable rather than local to one component instance. It carries no backend
identity: no table, no security block, nothing persists to the database. It is scoped by *lifetime*, and that
scope word is a closed set of exactly two keywords.

⛔ **What a store cannot do TODAY is the whole reason to read this page before writing one.** Its declaration
resolves and emits cleanly, but no mechanism anywhere — resolver, emitter, or client runtime — lets a *component*
reach a store's name. Reading `store.Whatever` or calling `store.SomeAction()` from inside a component body fails
with "unknown identifier", exactly as if the store did not exist. See [[#gap]].

## Signature      {#signature}
```osy syntax
store app Name { … }       // one in-memory instance for the running app, gone on reload
store session Name { … }   // like `app`, but persisted across reloads and cleared on logout
```

## Description    {#description}

### The scope word — a closed set of exactly two keywords   {#scopes}
`store <scope> Name { … }` takes a scope immediately after the `store` keyword. It is a closed vocabulary of
**exactly two** words:

| Scope | Lifetime |
|---|---|
| `app` | One instance for the running app, in memory, gone on reload. |
| `session` | Like `app`, but persisted across reloads (cleared on logout). |

A third word is a compile error naming the two real options — it used to be accepted SILENTLY: any identifier that
was not `app` or `session` fell through to a third, unimplemented lifetime (`ComponentProvided` — see
[[#component-provided]]) with no diagnostic at all, so a typo of either keyword (`store sesion Cart { … }`)
compiled clean into a scope nothing could ever reach. That is fixed: an unrecognized scope word is refused with a
did-you-mean.

Both scopes declare and resolve cleanly — this half of `store` genuinely works:

```osy title="both scopes declare cleanly — state, a derived value, and an action" test app=ui-store
store app Toast {
  int Count = 0;
  live var any = Count > 0;
  action Error(string msg) { Count = Count + 1; }
}

store session Wizard {
  string Step = "start";
  action Advance(string step) { Step = step; }
}
```

### Can a component read or call one today?   {#gap}
**No.** A store's name is registered nowhere a component body looks names up — only routed/composable
`component`s are wired into the name table a component's body resolves against. Reaching for the store by its
declared name from inside a component is refused as an unknown identifier, the same refusal an author would get
for any name that was never declared:

```osy title="✗ a component cannot reach a store's name — measured, not a hypothetical" syntax
store app Toast {
  int Count = 0;
  action Error(string msg) { Count = Count + 1; }
}

component ToastCheck() {
  action Boom() { Toast.Error("bad"); }   // error: unknown identifier 'Toast'
  render { Stack { Text($"{Toast.Count}"); } }   // error: unknown identifier 'Toast'
}
```

So today, `store` is a place to write shared reactive logic down, but not a place anything can reach it from.
**Until this lands, reach for one of these instead:**
- **Shared, stateless logic** (formatting, a computation more than one component needs) — a `public` method on a
  [class](https://osysharp.com/reference/class/methods/), held as a field on whichever component needs it. This part is real and works today —
  see [[ui-action#choosing]].
- **Shared, PERSISTED truth** (a cart's contents, anything that should survive a refresh and be visible to more
  than the browser tab that wrote it) — an `entity` with a reactive `live var` query, not UI state at all. This is
  what [component](https://osysharp.com/reference/ui/component/) calls "most 'global state' is actually backend data."
- **Genuinely ephemeral, cross-component UI-only state** (is the command palette open, a toast queue, an
  in-progress wizard that must NOT touch the database) has no working substitute today — that gap is real, and
  `store` is the intended answer once the read/invoke path is built.

### The third scope — reserved, not usable   {#component-provided}
The underlying model (`UiStoreScope`) carries a THIRD lifetime beyond `app`/`session`: a store a component
instantiates and provides to its own subtree, injected by type — for control-internal shared state (a multi-step
form's fields shared across its own inputs; a grid's selection). This is a real, deliberate design point, not a
guess: `store`'s own scopes are lifetimes, and "shared with descendants of one component instance" is the third
lifetime a scoped-state primitive naturally has.

It has **no syntax today** — no keyword spells it, nothing declares it, and no client runtime reads it. Do not
guess at a spelling for it; none has been decided. The enum value stays reserved for when it is actually built,
and the closed-set check above ([[#scopes]]) is what stops an attempt at writing it from being silently
misread as something else.

## See also       {#see-also}
- [component](https://osysharp.com/reference/ui/component/) — the member grammar a store shares (`state`/`derived`/`[live] data`/`effect`/`action`/
  `method`), and where "most global state is actually backend data" is explained in full.
- [action vs. method](https://osysharp.com/reference/ui/action/) — the `action`/`method` distinction, unchanged inside a store.
- [class methods](https://osysharp.com/reference/class/methods/) — the reuse mechanism that DOES work today for shared, stateless logic.
