# System Mute Indicator

Detect when a participant's microphone or camera is silenced by something outside the participant's control.
The operating system muting the track, a Bluetooth headset disconnecting, an iOS audio session interruption, or transient issues.

The default `ToggleAudioPublishingButton` and `ToggleVideoPublishingButton` shipped with the SDK already do this out of the box.
This guide is for cases where you build custom call controls or a [custom participant label](https://getstream.io/video/docs/react/v2/ui-cookbook/participant-label/) and want the same behavior.

>
> **Info:** For remote participants, interruption tracking is currently surfaced for `TrackType.AUDIO` only. Remote video and screen-share interruption are not tracked. For the local participant, both audio and video are covered.
>

## Best Practices

- Show the indicator only when the user did **not** intentionally mute. Distinguish "paused by your system" from "muted by choice."
- Place the indicator next to the relevant control (mic toggle or camera toggle) so the cause-and-effect is clear.
- Don't disable the toggle button — let the user attempt to recover once the underlying cause clears (for example, reconnecting headphones).
- Dismiss the indicator automatically once the track resumes producing media. The underlying `interruptedTracks` array updates reactively, so simply re-reading the helper on each render is enough.

## Detecting interruption

Use the `hasInterruptedTrack(participant, trackType)` helper. It returns `true` when the participant intends to publish the track (it is in `publishedTracks`) but no media is flowing right now (it is in `interruptedTracks`).

```tsx
import {
  hasInterruptedTrack,
  SfuModels,
  useCallStateHooks,
} from "@stream-io/video-react-sdk";

const useIsMicInterrupted = () => {
  const { useLocalParticipant } = useCallStateHooks();
  const localParticipant = useLocalParticipant();
  if (!localParticipant) return false;
  return hasInterruptedTrack(localParticipant, SfuModels.TrackType.AUDIO);
};
```

The same pattern works for video. Swap `TrackType.AUDIO` for `TrackType.VIDEO`.

>
> **Info:** Prefer `hasInterruptedTrack` over reading `participant.interruptedTracks` directly. The helper intersects with `publishedTracks`, so the indicator clears immediately when the participant unpublishes the track instead of lingering until a separate `unmute` event arrives.
>

## Building a custom mic-status indicator

Wrap the hook into a small component that renders next to your microphone toggle:

```tsx
import {
  hasInterruptedTrack,
  SfuModels,
  useCallStateHooks,
} from "@stream-io/video-react-sdk";

export const MicSystemMuteIndicator = () => {
  const { useLocalParticipant } = useCallStateHooks();
  const localParticipant = useLocalParticipant();
  if (!localParticipant) return null;

  const isMicInterrupted = hasInterruptedTrack(
    localParticipant,
    SfuModels.TrackType.AUDIO,
  );

  if (!isMicInterrupted) return null;
  return (
    <span role="status" aria-live="polite">
      Microphone is paused by your system
    </span>
  );
};
```

Drop it into a custom control bar alongside your toggle buttons:

```tsx
const CustomCallControls = () => {
  return (
    <div className="call-controls">
      <ToggleAudioPublishingButton />
      <MicSystemMuteIndicator />
      <ToggleVideoPublishingButton />
    </div>
  );
};
```

## Reflecting interruption on the toggle button itself

If you build a custom mic toggle (instead of using `ToggleAudioPublishingButton`), reflect interruption in the button's tooltip rather than its on/off state.
The participant has not turned the mic off, the system did:

```tsx
import {
  hasInterruptedTrack,
  SfuModels,
  useCallStateHooks,
} from "@stream-io/video-react-sdk";

const CustomMicToggle = () => {
  const { useMicrophoneState, useLocalParticipant } = useCallStateHooks();
  const { microphone, isMute } = useMicrophoneState();
  const localParticipant = useLocalParticipant();

  const isSystemMuted =
    !!localParticipant &&
    hasInterruptedTrack(localParticipant, SfuModels.TrackType.AUDIO);

  const title = isSystemMuted
    ? "Microphone is paused by your system"
    : isMute
      ? "Turn on microphone"
      : "Turn off microphone";

  return (
    <button title={title} onClick={() => microphone.toggle()}>
      {/* your icon */}
    </button>
  );
};
```

This mirrors what the SDK's built-in `ToggleAudioPublishingButton` does internally.

## See also

- [Call and participant state](https://getstream.io/video/docs/react/v2/guides/call-and-participant-state/#detecting-interrupted-tracks) — the `interruptedTracks` field reference.
- [Speaking while muted](https://getstream.io/video/docs/react/v2/ui-cookbook/speaking-while-muted/) — a complementary hint for when the user is muted by choice but trying to speak.

---

For the most recent version of this documentation, visit [https://getstream.io/video/docs/react/v2/ui-cookbook/system-mute-indicator/](https://getstream.io/video/docs/react/v2/ui-cookbook/system-mute-indicator/).