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

# Hooks

> Let plugins extend other plugins

## Overview

Hooks allow plugins to expose **extension points** that other plugins can register handlers for. Unlike events (fire-and-forget), hooks collect return values.

```javascript theme={null}
// Toolbar plugin registers a hook
api.registerHook('toolbar:add-button', (config) => {
  const btn = document.createElement('button');
  btn.textContent = config.label;
  toolbar.appendChild(btn);
  return btn;
});

// Another plugin uses the hook
const buttons = api.useHook('toolbar:add-button', {
  label: 'My Action',
  action: () => alert('clicked!')
});
// buttons → [buttonElement, ...]
```

## API

### `api.registerHook(name, handler)`

Register a function that responds to a named hook.

| Parameter | Type       | Description                                    |
| --------- | ---------- | ---------------------------------------------- |
| `name`    | `string`   | Hook name (use `namespace:action` format)      |
| `handler` | `function` | Callback receiving payload, can return a value |

```javascript theme={null}
api.registerHook('sidebar:add-item', (config) => {
  const item = document.createElement('div');
  item.textContent = config.label;
  sidebarEl.appendChild(item);
  return item;
});
```

### `api.useHook(name, payload)`

Call all registered handlers for a hook. Returns an array of their return values.

| Parameter | Type     | Description                 |
| --------- | -------- | --------------------------- |
| `name`    | `string` | Hook name to invoke         |
| `payload` | `any`    | Data passed to each handler |

**Returns:** `Array<any>` — return values from each handler (empty array if none registered).

```javascript theme={null}
const items = api.useHook('sidebar:add-item', {
  label: 'Settings',
  icon: '⚙️',
  onClick: () => openSettings()
});
// items → [divElement, divElement, ...]
```

### `api.removeHook(name, handler)`

Remove a previously registered handler. Must pass the same function reference.

```javascript theme={null}
function myHandler(config) {
  // ...
  return result;
}

api.registerHook('my-hook', myHandler);
// Later:
api.removeHook('my-hook', myHandler);
```

<Tip>
  Call `removeHook` in your `teardown()` function to clean up extension points when your plugin is disabled.
</Tip>

## Hooks vs Events

|                   | Events               | Hooks                        |
| ----------------- | -------------------- | ---------------------------- |
| **Purpose**       | Notify               | Extend                       |
| **Return values** | No                   | Yes                          |
| **Direction**     | One-way broadcast    | Request → responses          |
| **Use case**      | "Something happened" | "Add your thing to my thing" |

## When to Use Each

### Use Events When:

* You're broadcasting that something happened
* You don't need a response
* Multiple plugins might be interested
* Example: `todo:added`, `theme:changed`

### Use Hooks When:

* You're exposing an extension point
* You need return values from handlers
* You want plugins to contribute to your UI
* Example: `toolbar:add-button`, `sidebar:add-section`

## Timing

Hooks are typically registered during `setup()`, but they're also commonly invoked **after all plugins have loaded**. Use the `board:allPluginsLoaded` event for this:

```javascript theme={null}
export function setup(api) {
  const toolbar = document.createElement('div');

  // Register my extension point
  api.registerHook('toolbar:add-button', (config) => {
    const btn = document.createElement('button');
    btn.textContent = config.label;
    btn.onclick = config.action;
    toolbar.appendChild(btn);
    return btn;
  });

  // After all plugins are loaded, let them add buttons
  api.bus.once('board:allPluginsLoaded', () => {
    api.useHook('toolbar:add-button', {
      label: 'Default',
      action: () => {}
    });
  });

  api.boardEl.appendChild(toolbar);
}
```

## Example: Extensible Sidebar

```javascript theme={null}
// === sidebar/plugin.js ===
export function setup(api) {
  const sidebar = document.createElement('div');
  sidebar.style.cssText = `
    position: fixed; top: 60px; right: 10px;
    display: flex; flex-direction: column; gap: 8px;
  `;

  // Let plugins add sections
  api.registerHook('sidebar:add-section', (config) => {
    const section = document.createElement('div');
    section.innerHTML = `<h4>${config.title}</h4>`;
    sidebar.appendChild(section);
    return section;
  });

  // Let plugins add items
  api.registerHook('sidebar:add-item', (config) => {
    const item = document.createElement('button');
    item.textContent = `${config.icon} ${config.label}`;
    item.onclick = config.onClick;
    sidebar.appendChild(item);
    return item;
  });

  api.boardEl.appendChild(sidebar);
}

// === my-plugin/plugin.js ===
export function setup(api) {
  api.useHook('sidebar:add-section', { title: 'My Tools' });
  api.useHook('sidebar:add-item', {
    label: 'Settings',
    icon: '⚙️',
    onClick: () => openSettings()
  });
}
```
