@fcannizzaro/streamdeck-react

Settings Hooks

Bi-directional settings sync between React state and the Stream Deck SDK.

useSettings

Returns a [settings, setSettings] tuple with shallow-merge semantics. Changes are synced bidirectionally between the React tree and the Stream Deck SDK.

function useSettings<S extends JsonObject = JsonObject>(): [S, (partial: Partial<S>) => void];

Merge Semantics

setSettings does a shallow merge ({ ...current, ...partial }), matching the Stream Deck SDK's setSettings behavior. This is intentionally different from React's useState -- it always merges, never replaces.

Example

type MySettings = { color: string; brightness: number };

function MyKey() {
  const [settings, setSettings] = useSettings<MySettings>();

  useKeyDown(() => {
    setSettings({ brightness: Math.min(100, settings.brightness + 10) });
  });

  return (
    <div
      style={{
        width: "100%",
        height: "100%",
        background: settings.color,
        opacity: settings.brightness / 100,
        alignItems: "center",
        justifyContent: "center",
      }}
    >
      <span style={{ color: "white", fontSize: 20 }}>{settings.brightness}%</span>
    </div>
  );
}

Sync Flow

  1. React to SDK: calling setSettings({ count: 5 }) updates the internal React state (triggers re-render) and calls action.setSettings() on the SDK to persist.
  2. SDK to React: when the Property Inspector saves settings (triggering onDidReceiveSettings), the internal state updates and components using useSettings() re-render.
  3. Conflict resolution: the SDK is the source of truth. Last write wins.

useGlobalSettings

Same pattern as useSettings, but for plugin-wide global settings shared across all action instances.

function useGlobalSettings<G extends JsonObject = JsonObject>(): [G, (partial: Partial<G>) => void];
type GlobalConfig = { apiKey: string; theme: "light" | "dark" };

function MyKey() {
  const [global] = useGlobalSettings<GlobalConfig>();

  return (
    <div
      style={{
        width: "100%",
        height: "100%",
        background: global.theme === "dark" ? "#000" : "#fff",
        alignItems: "center",
        justifyContent: "center",
      }}
    >
      <span style={{ color: global.theme === "dark" ? "#fff" : "#000", fontSize: 14 }}>
        {global.apiKey ? "Connected" : "No API Key"}
      </span>
    </div>
  );
}

See also Settings & Property Inspector for the full sync architecture.

On this page