> ## Documentation Index
> Fetch the complete documentation index at: https://empty-ad9a3406.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Storage

> Persist plugin data with localStorage

## Overview

`api.storage` is a thin wrapper over `localStorage` with automatic JSON serialization.

```javascript theme={null}
api.storage.set('my-counter', 42);
const count = api.storage.get('my-counter');  // → 42
```

## API

### `api.storage.get(key)`

Retrieve a value. Returns `null` if the key doesn't exist.

```javascript theme={null}
const theme = api.storage.get('my-plugin-theme');
// → "dark" or null
```

### `api.storage.set(key, value)`

Store a value. Automatically serializes to JSON. Emits a `storage:change` event.

```javascript theme={null}
api.storage.set('my-counter', 42);
api.storage.set('my-list', [1, 2, 3]);
api.storage.set('my-obj', { name: 'test', active: true });
```

### `api.storage.remove(key)`

Delete a stored key.

```javascript theme={null}
api.storage.remove('my-counter');
```

### `api.storage.list()`

Returns an array of all localStorage keys.

```javascript theme={null}
const keys = api.storage.list();
// → ['board-plugins-registry', 'my-counter', ...]
```

### `api.storage.clear()`

Clears all localStorage data. Use with extreme caution.

## Plugin-Scoped Storage

Instead of manually prefixing keys, use the plugin-scoped methods. They automatically prefix with `plugin:{pluginId}:`.

### `api.storage.getForPlugin(pluginId, key)`

```javascript theme={null}
const theme = api.storage.getForPlugin(meta.id, 'theme');
// Reads from localStorage key: "plugin:my-plugin:theme"
```

### `api.storage.setForPlugin(pluginId, key, value)`

```javascript theme={null}
api.storage.setForPlugin(meta.id, 'theme', 'dark');
// Writes to localStorage key: "plugin:my-plugin:theme"
```

<Info>
  The Sample Theme plugin uses `setForPlugin`/`getForPlugin` to persist theme settings with automatic key scoping.
</Info>

## Storage Change Events

Every `set()` and `setForPlugin()` call emits a `storage:change` event:

```javascript theme={null}
api.bus.on('storage:change', ({ key, value, oldValue, pluginId }) => {
  console.log(`${key} changed from`, oldValue, 'to', value);
  if (pluginId) console.log(`Changed by plugin: ${pluginId}`);
});
```

| Field      | Description                                                               |
| ---------- | ------------------------------------------------------------------------- |
| `key`      | The storage key (full key, including `plugin:` prefix for scoped storage) |
| `value`    | The new value                                                             |
| `oldValue` | The previous value                                                        |
| `pluginId` | *(only for `setForPlugin`)* The plugin that made the change               |

## Best Practices

### Use Plugin-Scoped Storage

```javascript theme={null}
// ✅ Good — automatic scoping, no collisions
api.storage.setForPlugin(meta.id, 'theme', 'dark');
api.storage.getForPlugin(meta.id, 'theme');

// ⚠️ Works, but manual prefixing
api.storage.set(`${meta.id}-theme`, 'dark');
api.storage.get(`${meta.id}-theme`);
```

### Store Positions

```javascript theme={null}
// Save position on drag end
api.bus.on('plugin:dragend', ({ el }) => {
  if (el.dataset.pluginId === meta.id) {
    api.storage.setForPlugin(meta.id, 'pos', {
      left: parseInt(el.style.left),
      top: parseInt(el.style.top)
    });
  }
});

// Restore on load
const pos = api.storage.getForPlugin(meta.id, 'pos');
if (pos) {
  api.container.style.left = pos.left + 'px';
  api.container.style.top = pos.top + 'px';
}
```

### Store Settings

```javascript theme={null}
// With defaults
const is24h = api.storage.getForPlugin(meta.id, '24h') ?? false;
const showSeconds = api.storage.getForPlugin(meta.id, 'seconds') ?? true;
```

<Warning>
  `api.storage` uses `localStorage`, which has a \~5MB limit per origin. Don't store large blobs (images, long documents) — use IndexedDB for that.
</Warning>

<Info>
  The core registry uses `board-plugins-registry` — avoid that key.
</Info>
