Skip to content
Platform docs
Auth, users, webhooks & more

Call & Participant State

You can access call, participant, and client state using hooks. These hooks are reactive (their value is updated on WebSocket events and API calls).

Best Practices

  • Always load the call (call.get() or call.join()) before accessing state - hooks return empty values otherwise.
  • Use useCall() to get the Call instance for API calls.
  • Access hooks through useCallStateHooks() for type-safe destructuring.
  • Filter participants by role or track type using utility functions like hasVideo().
  • Use useRawParticipants when sorting isn't needed to reduce renders.

Call State

To observe call state, you need to provide a Call instance to the StreamCall component.

Note:

For the best experience, please make sure that the provided Call instance is loaded and connected to our backend: Load Call.

Otherwise, call.state and the call state hooks will provide empty values.

Example:

import {
  Call,
  StreamCall,
  useCall,
  useCallStateHooks,
} from "@stream-io/video-react-sdk";

export default function MyApp() {
  let call: Call;

  return (
    <StreamCall call={call}>
      <MyCallUI />
    </StreamCall>
  );
}

const MyCallUI = () => {
  const call = useCall();

  const { useCallCallingState, useParticipants } = useCallStateHooks();
  const callingState = useCallCallingState();
  const participants = useParticipants();

  return (
    <div>
      <div>Call: {call?.cid}</div>
      <div>State: {callingState}</div>
      <div>Participants: {participants.length}</div>
    </div>
  );
};

StreamCall is a context provider. useCall returns the Call instance for API calls.

Call State Hooks

Name Description
useCall The Call instance that is registered with StreamCall. You need the Call instance to initiate API calls.
useCallBlockedUserIds The list of blocked user IDs.
useCallCallingState Provides information about the call state. For example, RINGING, JOINED or RECONNECTING.
useCallClosedCaptions The closed captions of the call.
useCallCreatedAt The time the call was created.
useCallCreatedBy The user that created the call.
useCallCustomData The custom data attached to the call.
useCallEgress The egress information of the call.
useCallEndedAt The time the call was ended.
useCallEndedBy The user that ended the call.
useCallIngress The ingress information of the call.
useCallMembers The list of call members
useCallSession The information for the current call session.
useCallSettings The settings of the call.
useCallStartedAt The actual start time of the current call session.
useCallStartsAt The scheduled start time of the call.
useCallStatsReport When stats gathering is enabled, this observable will emit a new value at a regular (configurable) interval.
useCallThumbnail The thumbnail of the call.
useCallUpdatedAt The time the call was last updated.
useCameraState The camera state of the local participant.
useDominantSpeaker The participant that is the current dominant speaker of the call.
useE2eeEnabled true if end-to-end encryption is active for the call.
useHasOngoingScreenShare It will return true if at least one participant is sharing their screen.
useHasPermissions Returns true if the local participant has all the given permissions.
useIncomingVideoSettings The state of manual overrides to incoming video quality.
useIsAutoplayBlocked true when the browser's autoplay policy is blocking audio playback. Use call.resumeAudio() to unblock.
useIsCallCaptioningInProgress true if the call is being close-captioned.
useIsCallHLSBroadcastingInProgress true if the call is being broadcasted in HLS mode.
useIsCallIndividualRecordingInProgress true if the indivudal track recording is running.
useIsCallLive true if the call is currently live.
useIsCallRawRecordingInProgress true if the raw recording is currently running.
useIsCallRecordingInProgress true if the call is being recorded.
useIsCallTranscribingInProgress true if the call is being transcribed.
useMicrophoneState The microphone state of the local participant.
useOwnCapabilities The capabilities of the local participant.
useScreenShareState The screen share state of the local participant.
useSpeakerState The speaker state of the local participant.

Destructure useCallStateHooks for the full list:

import { useCallStateHooks } from "@stream-io/video-react-sdk";

const {
  useCallMembers,
  useDominantSpeaker,
  useParticipants,
  useLocalParticipant,
  useIsCallRecordingInProgress,
  // ...
} = useCallStateHooks();

Participant state

Participant hooks return StreamVideoParticipant objects.

Participant State Hooks

Name Description
useAnonymousParticipantCount The approximate participant count of anonymous users in the active call.
useLocalParticipant The local participant is the logged-in user.
useParticipantCount The approximate participant count of the active call. This includes the anonymous users as well, it is computed on the server-side.
useParticipants All participants, including local and remote participants.
usePinnedParticipants The participants that are currently pinned.
useRawParticipants A version of useParticipants that is not affected by participant sort settings and thus causes less component updates.
useRemoteParticipants All participants except the local participant.
Warning:

Warning: In a call with many participants, the value of the useParticipants() is truncated to 250 participants.

The participants who are publishing video, audio, or screen sharing have priority over the other participants in the list. This means, for example, that in a livestream with one host and many viewers, the host is guaranteed to be in the list.

import {
  useCallStateHooks,
  Call,
  StreamCall,
} from "@stream-io/video-react-sdk";

export default function App() {
  let call: Call;

  return (
    <StreamCall call={call}>
      <MyCallUI />
    </StreamCall>
  );
}

const MyCallUI = () => {
  const { useLocalParticipant, useParticipantCount } = useCallStateHooks();
  const participantCount = useParticipantCount();
  const localParticipant = useLocalParticipant();

  return (
    <div>
      <div>Number of participants: {participantCount}</div>
      <div>Session ID: {localParticipant.sessionId}</div>
    </div>
  );
};

Participant data

The StreamVideoParticipant object has the following properties:

Name Description
audioLevel The audio level of the participant (determined on the server).
audioStream The audio MediaStream.
audioVolume The audio volume level of the participant (overridable local audioVolume level).
connectionQuality The participant's connection quality.
custom The participant's custom data. Comes from the custom field of the user object.
image The image of the participant.
interruptedTracks Tracks the participant intends to publish but that are currently not producing media (system mute, OS-level kill switch, Bluetooth disconnect, iOS audio interruption, sustained RTP stalls). Orthogonal to publishedTracks.
isDominantSpeaker It's true if the participant is the current dominant speaker in the call.
isLocalParticipant It's true if the participant is the local participant.
isSpeaking It's true if the participant is currently speaking.
joinedAt The time the participant joined the call.
name The name of the participant.
pausedTracks The tracks that are currently server-side paused for the local participant.
pin Holds pinning information.
publishedTracks The track types the participant is currently publishing
reaction The last reaction this user has sent to this call.
roles The roles of the participant in this call.
screenShareAudioStream The screen share audio MediaStream.
screenShareStream The screen share MediaStream.
sessionId The identifier of the participant within the existing call session
source The participant source: WebRTC (default), RTMP (OBS), WHIP, SIP, RTSP, SRT...
userId The user ID of the participant.
videoStream The video MediaStream.
viewportVisibilityState The viewport visibility state of the participant.

Utility functions

import {
  hasAudio,
  hasVideo,
  hasScreenShare,
  hasScreenShareAudio,
  hasInterruptedTrack,
  hasPausedTrack,
  isPinned,
  SfuModels,
  useCallStateHooks,
} from "@stream-io/video-react-sdk";

// example usage
const { useParticipants } = useCallStateHooks();
const participants = useParticipants();

// check if the participant has audio, video, screen share or screen share audio
const [participant] = participants;
const hasAudioOn = hasAudio(participant);
const hasVideoOn = hasVideo(participant);
const hasScreenShareOn = hasScreenShare(participant);
const hasScreenShareAudioOn = hasScreenShareAudio(participant);
const isPinnedOn = isPinned(participant);
const isVideoPaused = hasPausedTrack(participant, "videoTrack");
const isAudioInterrupted = hasInterruptedTrack(
  participant,
  SfuModels.TrackType.AUDIO,
);

// participants with a specific role
const hosts = participants.filter((p) => p.roles.includes("host"));

// participants that publish video and audio
const videoParticipants = participants.filter(
  (p) => hasVideo(p) && hasAudio(p),
);

Detecting participant source

The source property indicates how participants joined (WebRTC, RTMP/OBS, WHIP, SIP, etc.):

import { SfuModels } from "@stream-io/video-react-sdk";

const { useParticipants } = useCallStateHooks();
const participants = useParticipants();

// participants joining through OBS have RTMP source
const rtmpParticipants = participants.filter(
  (p) => p.source === SfuModels.ParticipantSource.RTMP,
);

Detecting interrupted tracks

A participant's published audio or video can be temporarily silenced by something outside the participant's control — the operating system muting the microphone, a Bluetooth headset disconnecting, an iOS audio session interruption, or transient SFU/RTP issues. Use the hasInterruptedTrack(participant, trackType) helper to detect this and surface a hint in the UI:

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

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

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

  if (!isMicInterrupted) return null;
  return <div>Microphone is paused by your system</div>;
};

The helper intersects interruptedTracks with publishedTracks, so the indicator only fires while the participant is actively publishing the track. This avoids a stale "interrupted" indicator if a remote sender unpublishes the track while it was interrupted.

For remote participants, interruptedTracks currently only surfaces TrackType.AUDIO; remote video and screen-share interruption are not tracked. For the local participant, it covers both audio and video.

The built-in ToggleAudioButton and ToggleVideoButton already use this signal to show a "paused by your system" tooltip — use the snippet above if you build custom controls. See the System Mute Indicator cookbook for a full worked example.

Client state

Use useConnectedUser to observe the connected user:

import {
  useConnectedUser,
  StreamVideo,
  StreamVideoClient,
} from "@stream-io/video-react-sdk";

export default function App() {
  let client: StreamVideoClient;

  return (
    <StreamVideo client={client}>
      <MyHeader />
    </StreamVideo>
  );
}

const MyHeader = () => {
  const user = useConnectedUser();
  return <div>{user ? `Logged in: ${user.name}` : "Logged out"}</div>;
};

Client state hooks

Name Description
useStreamVideoClient The StreamVideoClient instance.
useConnectedUser Returns the connected user. Holds the server-side data of the connected user.
useCalls A list of all tracked calls. These calls can be outgoing (I have called somebody) or incoming (somebody has called me). Loaded calls (call.get()) are also part of this list.

The connectedUser object contains the following properties:

Name Description
created_at The time the user was created.
custom Custom user data.
deleted_at The time the user was deleted.
devices The registered push notification devices of the user.
id The id of the user.
image The profile image of the user.
name The name of the user.
role The role of the user.
teams The teams the user belongs to.
updated_at The time when the user was updated.