Client Hooks

Last Updated: October 2026

Client hooks are a browser-side event system that plugins use to modify UI behaviour, share data, or communicate with each other. They mirror the server hook API but run entirely in the browser.

Overview

Client hooks are accessed via window.__pokerEngine.hooks:

const { hooks } = window.__pokerEngine;

// Filter — receives a value, must return it (possibly modified)
hooks.addFilter('hook-name', (value, ...args) => {
  return modifiedValue;
}, options);

// Action — fire-and-forget, return value is ignored
hooks.addAction('hook-name', (...args) => {
  doSomething();
}, options);

All hooks registered with a providerType are automatically removed when the plugin is deactivated. Always pass providerType in options.

ClientHookRegistry API

addFilter(hook, callback, options?)

Registers a filter callback. Filters run in priority order — each callback receives the value returned by the previous one. Filters are synchronous, so do not return a Promise. If a callback throws, the error is logged and the chain continues with the previous value.

hooks.addFilter('my-plugin:format-balance', (amount, currency) => {
  return `${currency} ${parseFloat(amount).toFixed(2)}`;
}, { providerType: 'my-plugin', priority: 10 });
ParameterTypeDescription
hookstringHook name
callbackfunction(value, ...args) => value — must return the value
options.prioritynumberExecution order — lower runs first (default: 10)
options.providerTypestringYour plugin’s providerType — required for cleanup

applyFilters(hook, value, ...args)

Runs all registered filter callbacks for a hook and returns the final value. Used by the host app and by plugins to apply a filter chain.

const formattedAmount = hooks.applyFilters('my-plugin:format-balance', '100.00', 'USD');

addAction(hook, callback, options?)

Registers an action callback. Actions are fire-and-forget — return values are ignored. Callbacks may be async.

hooks.addAction('my-plugin:deposit-complete', (deposit) => {
  analytics.track('deposit', { amount: deposit.amount });
}, { providerType: 'my-plugin', priority: 10 });

doAction(hook, ...args)

Fires all registered action callbacks for a hook in priority order, awaiting each one. Returns a Promise<void> that resolves after all callbacks (including async ones) complete. A callback that throws is logged and does not stop the others.

await hooks.doAction('my-plugin:deposit-complete', { amount: '100.00' });

removeAction(hook, callback) / removeFilter(hook, callback)

Removes a specific callback by reference. Prefer using providerType for bulk cleanup on deactivation.

unregisterPlugin(providerType)

Removes all actions and filters registered by a given providerType. Called automatically by usePluginSync when a plugin:deactivated, plugin:auto-disabled or plugin:updated event is received.

Host Application Hooks

The following hooks are fired by the host application and can be consumed by plugins.

Filters the list of user-facing plugin navigation items before they are rendered in the main sidebar. Plugins can use this hook to add, remove, reorder, or modify nav items.

Value type: Array<{ key: string; to: string; providerType: string; path: string; icon?: string; label: string; badge?: string }>

hooks.addFilter('nav:items:user', (items) => {
  // Remove a specific page from the nav
  return items.filter(item =>
    !(item.providerType === 'my-plugin' && item.path === '/hidden-page')
  );
}, { providerType: 'my-plugin', priority: 10 });

Inter-Plugin Hooks

In addition to host application hooks, the client hook registry serves as an inter-plugin communication bus. Plugins can define and fire their own hooks for coordination.

Pattern: Plugin A exposes a hook that Plugin B consumes

Plugin A (fires the hook):

// In plugin-a's frontend entry
const { hooks } = window.__pokerEngine;

async function loadTableData(tableId) {
  const rawData = await fetchTable(tableId);

  // Let other plugins transform the data before rendering
  const processedData = hooks.applyFilters('plugin-a:table-data', rawData, { tableId });

  renderTable(processedData);
}

Plugin B (consumes the hook):

// In plugin-b's frontend entry
const { hooks } = window.__pokerEngine;

hooks.addFilter('plugin-a:table-data', (data, context) => {
  // Inject extra field from plugin-b
  return { ...data, customField: computeCustomField(data) };
}, { providerType: 'plugin-b', priority: 10 });

Pattern: Broadcast events between plugins

Plugin A (broadcasts):

hooks.doAction('plugin-a:user-identified', { userId: '123', tier: 'vip' });

Plugin B (listens):

hooks.addAction('plugin-a:user-identified', ({ userId, tier }) => {
  updateUserDisplay(userId, tier);
}, { providerType: 'plugin-b' });

Naming Conventions

Use {providerType}:{event-name} for plugin-defined hooks to avoid collisions:

my-plugin:settings-loaded     ✓
my-plugin:user-identified     ✓
settings-loaded               ✗  (too generic, may collide)
user                          ✗  (not descriptive enough)

Lifecycle Sync

Client hooks are synchronised with the backend plugin state via WebSocket events. When a plugin is deactivated (manually or via circuit breaker) or upgraded, the usePluginSync hook calls:

fillRegistry.unregisterPlugin(providerType);
pageRegistry.unregisterPlugin(providerType);
clientHooks.unregisterPlugin(providerType);

This removes all the plugin’s registered hooks, slot fills, and pages from the frontend without a page reload, along with its injected script tags. On upgrade, the new bundle is then loaded.

ResourceLink
Plugin Development Guidedeveloper/plugins
Server Hooks Referencedeveloper/plugins-hooks-server
Slot Fills Referencedeveloper/plugins-slot-fills
Hook registry sourceclient/src/core/hooks/client-hook-registry.ts
Plugin sync sourceclient/src/hooks/usePluginSync.ts
Try the live demo Pricing

Have questions about Poker Engine? We reply fast.