Skip to content

Shared Slot State

Use the slot-state helpers to share state between the EXTENSION_SLOT_BACKGROUND slot and visual slots. The background slot owns the state. Visual slots read it and send partial updates through the same channel.

The state is available only to slots of the current extension client. It is not persistent storage or a broadcast to other viewers.

Roles

SlotHelperResponsibility
Background slotcreateSlotStateHostOwns and updates the shared state.
Visual slotcreateSlotStateClientReads and updates the shared state.

Create one host for each channel in the background slot. Create a client in each visual slot that needs the state. Every participant must use the same channel string.

Example: Shared Counter

This example uses one background slot and one React visual slot. Additional visual slots can create clients with the same channel: 'score'.

1. Create the host in the background slot

The background slot is non-visual and remains available to coordinate the extension. It owns the shared score and does not render UI.

ts
import { createExtHelper } from '@stripchatdev/ext-helper';
import { createSlotStateHost } from '@stripchatdev/ext-helper/helpers';

type ScoreState = {
  score: number;
};

const extHelper = createExtHelper();

export const init = () => {
  const scoreState = createSlotStateHost<ScoreState>({
    channel: 'score',
    extHelper,
    initialState: { score: 0 },
  });

  return () => scoreState.unsubscribe();
};

Create the host once in the background slot. The background slot can also call scoreState.setState() when its own event handlers change the state.

2. Create a client in a visual slot

The visual slot reads the latest background snapshot and sends partial updates back to the host. It does not own the score.

Example in React:

tsx
import { createExtHelper } from '@stripchatdev/ext-helper';
import { useSlotState } from '@stripchatdev/ext-helper/helpers/react';

type ScoreState = {
  score: number;
};

const extHelper = createExtHelper();

export function ScorePanel() {
  const { isLoading, state, setState } = useSlotState<ScoreState>({
    channel: 'score',
    extHelper,
  });

  if (isLoading || !state) {
    return <p>Loading score...</p>;
  }

  return (
    <button onClick={() => void setState({ score: state.score + 1 })}>
      Score: {state.score}
    </button>
  );
}

The client starts with isLoading: true and state: null. After the initial state is available, setState() sends a shallow partial update to the host and the client updates when the host publishes the new state. Nested objects must be updated explicitly.

Framework-Neutral Visual Slot

Use createSlotStateClient in a non-React visual slot. Subscribe to changes and read the current state with getSnapshot().

ts
import { createExtHelper } from '@stripchatdev/ext-helper';
import { createSlotStateClient } from '@stripchatdev/ext-helper/helpers';

const extHelper = createExtHelper();
const state = createSlotStateClient<{ score: number }>({
  channel: 'score',
  extHelper,
});

const unsubscribe = state.subscribe(() => {
  const snapshot = state.getSnapshot();

  if (!snapshot.isLoading && snapshot.state) {
    renderScore(snapshot.state.score);
  }
});

void state.setState({ score: 10 });

// Call this when the visual slot is disposed.
unsubscribe();

Rules

  • Create one host per shared channel in the background slot.
  • Create clients only in visual slots that need the state.
  • Use exactly the same channel string for the host and its clients.
  • Keep the background slot as the source of truth.
  • Use setState() for shallow partial updates.
  • Handle the initial isLoading and state: null values.
  • Unsubscribe clients when their visual slot is disposed.
  • Unsubscribe the host when the background slot is torn down.