> ## 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.

# Persisting Data

> Store and restore plugin state with api.storage

## Basic Usage

```javascript theme={null}
// Save
api.storage.set('my-key', { count: 42, items: ['a', 'b'] });

// Load
const data = api.storage.get('my-key');
// → { count: 42, items: ['a', 'b'] }

// Delete
api.storage.remove('my-key');
```

## Plugin-Scoped Storage (Recommended)

Use `getForPlugin` and `setForPlugin` to automatically namespace your keys:

```javascript theme={null}
// Automatically stored under "plugin:my-plugin:theme"
api.storage.setForPlugin(meta.id, 'theme', 'dark');
const theme = api.storage.getForPlugin(meta.id, 'theme');
// → "dark"
```

This prevents key collisions between plugins without manual prefixing.

## Common Patterns

### Saving Element Positions

```javascript theme={null}
let currentApi = null;

export function setup(api) {
  currentApi = api;
  const container = api.container;

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

  // Save 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)
      });
    }
  });
}
```

### Saving Lists

```javascript theme={null}
export function setup(api) {
  let items = api.storage.getForPlugin(meta.id, 'items') || [];

  function addItem(text) {
    items.push({ text, done: false, id: Date.now() });
    api.storage.setForPlugin(meta.id, 'items', items);
  }

  function removeItem(id) {
    items = items.filter(i => i.id !== id);
    api.storage.setForPlugin(meta.id, 'items', items);
  }
}
```

### Saving Settings with Defaults

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

// Update and save
function toggle24h() {
  settings.is24h = !settings.is24h;
  api.storage.setForPlugin(meta.id, '24h', settings.is24h);
}
```

### Reacting to Storage Changes

```javascript theme={null}
// Listen for ANY storage change
api.bus.on('storage:change', ({ key, value, oldValue, pluginId }) => {
  console.log(`${key}: ${oldValue} → ${value}`);
});

// Listen for changes to your own plugin's storage
api.bus.on('storage:change', ({ key, value }) => {
  if (key === `plugin:${meta.id}:theme`) {
    applyTheme(value);
  }
});
```

## Key Naming

When using plugin-scoped storage, you don't need to prefix manually:

```javascript theme={null}
// ✅ Good — automatic scoping
api.storage.setForPlugin(meta.id, 'theme', 'dark');
api.storage.setForPlugin(meta.id, 'items', [1, 2, 3]);

// ⚠️ Manual prefixing (still works)
api.storage.set(`${meta.id}-theme`, 'dark');
api.storage.set('sticky-notes-list', notes);
```

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

## Manual Prefix Keys (Legacy)

If you prefer manual prefixing (the older pattern), it still works:

```javascript theme={null}
// ✅ Good
api.storage.set('sticky-notes-list', notes);
api.storage.set('clock-24h', true);

// ❌ Bad — will collide
api.storage.set('data', notes);
api.storage.set('settings', { ... });
```

## Storage Limits

`localStorage` has a \~5MB limit per origin. For larger data, consider:

* Compressing before storing
* Using IndexedDB
* Storing only what's needed
