> ## Documentation Index
> Fetch the complete documentation index at: https://docs.streamwizard.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Widget state

> Keep counters, totals, and progress between streams with StreamWizard.state.

Widgets reload every time the overlay loads. Anything in a variable is gone.
`StreamWizard.state` stores a JSON blob per placed widget, so death counters
and sub goals survive OBS restarts and stream ends.

## The API

```js theme={null}
// Load what you saved last time (null on first run)
const state = await StreamWizard.state.get();
let deaths = state?.deaths ?? 0;

// Save it back (overwrites the whole blob)
await StreamWizard.state.set({ deaths });
```

Two calls. `get()` returns your saved object or `null`; `set()` replaces it.
Spread the old state if you're only changing one key.

## A complete counter

```js theme={null}
let deaths = 0;

window.addEventListener('onWidgetLoad', async () => {
  const state = await StreamWizard.state.get().catch(() => null);
  deaths = state?.deaths ?? 0;
  render();
});

window.addEventListener('onEventReceived', (e) => {
  if (e.detail.listener !== 'channel.channel_points_custom_reward_redemption.add') return;
  if (e.detail.event.reward.title !== 'Add death') return;
  deaths++;
  render();
  StreamWizard.state.set({ deaths });
});

function render() {
  document.getElementById('count').textContent = deaths;
}
```

## Good to know

* **Per placement**: two copies of the same widget on different overlays have
  separate state.
* **Editor preview has no state**: `get`/`set` throw there, because the
  preview isn't attached to an overlay. Wrap in `try/catch` or `.catch()` if
  you test in the editor a lot.
* **Don't save on every frame**: batch it. Save after a change settles, not
  inside an animation loop.
* Older widgets using `window.StreamWizard.stateUrl` with manual `fetch`
  still work; `state.get`/`set` is the same API with the plumbing done.
