Skip to main content
Manage call participants by muting, pinning, and monitoring their status during a call.

Get Participant List

Listen for participant list changes:
import { CometChatCalls } from '@cometchat/calls-sdk-react-native';

CometChatCalls.addEventListener('onParticipantListChanged', (participants) => {
  console.log('Participants:', participants);
  participants.forEach((participant) => {
    console.log(`- ${participant.name} (${participant.uid})`);
  });
});

Participant Object

Each participant contains:
PropertyTypeDescription
pidstringParticipant ID (unique per session)
uidstringUser ID
namestringDisplay name
avatarstringAvatar URL (optional)

Pin Participant

Pin a participant to the main view in Sidebar or Spotlight layouts:
// Pin a participant's video
CometChatCalls.pinParticipant(participantId, 'human');

// Pin a participant's screen share
CometChatCalls.pinParticipant(participantId, 'screen-share');
ParameterTypeDescription
participantIdstringThe participant’s pid
typestring'human' for video, 'screen-share' for screen share

Unpin Participant

Remove the pinned participant:
CometChatCalls.unpinParticipant();

Mute Participant

Mute another participant’s audio (requires moderator permissions):
CometChatCalls.muteParticipant(participantId);

Pause Participant Video

Pause another participant’s video (requires moderator permissions):
CometChatCalls.pauseParticipantVideo(participantId);

Participant Events

Join and Leave

CometChatCalls.addEventListener('onParticipantJoined', (participant) => {
  console.log(`${participant.name} joined the call`);
});

CometChatCalls.addEventListener('onParticipantLeft', (participant) => {
  console.log(`${participant.name} left the call`);
});

Audio State

CometChatCalls.addEventListener('onParticipantAudioMuted', (participant) => {
  console.log(`${participant.name} muted their audio`);
});

CometChatCalls.addEventListener('onParticipantAudioUnmuted', (participant) => {
  console.log(`${participant.name} unmuted their audio`);
});

Video State

CometChatCalls.addEventListener('onParticipantVideoPaused', (participant) => {
  console.log(`${participant.name} paused their video`);
});

CometChatCalls.addEventListener('onParticipantVideoResumed', (participant) => {
  console.log(`${participant.name} resumed their video`);
});

Hand Raised

CometChatCalls.addEventListener('onParticipantHandRaised', (participant) => {
  console.log(`${participant.name} raised their hand`);
});

CometChatCalls.addEventListener('onParticipantHandLowered', (participant) => {
  console.log(`${participant.name} lowered their hand`);
});

Screen Sharing

CometChatCalls.addEventListener('onParticipantStartedScreenShare', (participant) => {
  console.log(`${participant.name} started screen sharing`);
});

CometChatCalls.addEventListener('onParticipantStoppedScreenShare', (participant) => {
  console.log(`${participant.name} stopped screen sharing`);
});

Dominant Speaker

CometChatCalls.addEventListener('onDominantSpeakerChanged', (participant) => {
  console.log(`Dominant speaker: ${participant.name}`);
});

Using OngoingCallListener

Handle participant events through the call settings listener:
const callListener = new CometChatCalls.OngoingCallListener({
  onUserJoined: (user) => {
    console.log('User joined:', user.name);
  },
  onUserLeft: (user) => {
    console.log('User left:', user.name);
  },
  onUserListUpdated: (userList) => {
    console.log('Participant count:', userList.length);
  },
  onUserMuted: (user) => {
    console.log('User muted:', user.name);
  },
});

const callSettings = new CometChatCalls.CallSettingsBuilder()
  .setCallEventListener(callListener)
  .build();

Complete Example

import React, { useState, useEffect } from 'react';
import { View, FlatList, Text, TouchableOpacity, StyleSheet } from 'react-native';
import { CometChatCalls } from '@cometchat/calls-sdk-react-native';

interface Participant {
  pid: string;
  uid: string;
  name: string;
  avatar?: string;
}

function ParticipantList() {
  const [participants, setParticipants] = useState<Participant[]>([]);
  const [pinnedId, setPinnedId] = useState<string | null>(null);

  useEffect(() => {
    const unsubscribeList = CometChatCalls.addEventListener(
      'onParticipantListChanged',
      (list: Participant[]) => {
        setParticipants(list);
      }
    );

    const unsubscribeJoin = CometChatCalls.addEventListener(
      'onParticipantJoined',
      (participant: Participant) => {
        console.log(`${participant.name} joined`);
      }
    );

    const unsubscribeLeave = CometChatCalls.addEventListener(
      'onParticipantLeft',
      (participant: Participant) => {
        console.log(`${participant.name} left`);
        // Unpin if the pinned participant left
        if (pinnedId === participant.pid) {
          setPinnedId(null);
        }
      }
    );

    return () => {
      unsubscribeList();
      unsubscribeJoin();
      unsubscribeLeave();
    };
  }, [pinnedId]);

  const handlePin = (participant: Participant) => {
    if (pinnedId === participant.pid) {
      CometChatCalls.unpinParticipant();
      setPinnedId(null);
    } else {
      CometChatCalls.pinParticipant(participant.pid, 'human');
      setPinnedId(participant.pid);
    }
  };

  const handleMute = (participant: Participant) => {
    CometChatCalls.muteParticipant(participant.pid);
  };

  const renderParticipant = ({ item }: { item: Participant }) => (
    <View style={styles.participantItem}>
      <View style={styles.avatar}>
        <Text style={styles.avatarText}>
          {item.name.charAt(0).toUpperCase()}
        </Text>
      </View>
      <Text style={styles.name}>{item.name}</Text>
      <View style={styles.actions}>
        <TouchableOpacity
          style={[styles.actionButton, pinnedId === item.pid && styles.activeButton]}
          onPress={() => handlePin(item)}
        >
          <Text style={styles.actionText}>
            {pinnedId === item.pid ? 'Unpin' : 'Pin'}
          </Text>
        </TouchableOpacity>
        <TouchableOpacity
          style={styles.actionButton}
          onPress={() => handleMute(item)}
        >
          <Text style={styles.actionText}>Mute</Text>
        </TouchableOpacity>
      </View>
    </View>
  );

  return (
    <View style={styles.container}>
      <Text style={styles.header}>
        Participants ({participants.length})
      </Text>
      <FlatList
        data={participants}
        keyExtractor={(item) => item.pid}
        renderItem={renderParticipant}
        ListEmptyComponent={
          <Text style={styles.emptyText}>No other participants</Text>
        }
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#1a1a1a',
  },
  header: {
    color: '#fff',
    fontSize: 18,
    fontWeight: '600',
    padding: 16,
    borderBottomWidth: 1,
    borderBottomColor: '#333',
  },
  participantItem: {
    flexDirection: 'row',
    alignItems: 'center',
    padding: 12,
    borderBottomWidth: 1,
    borderBottomColor: '#333',
  },
  avatar: {
    width: 40,
    height: 40,
    borderRadius: 20,
    backgroundColor: '#6851D6',
    justifyContent: 'center',
    alignItems: 'center',
  },
  avatarText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: '600',
  },
  name: {
    flex: 1,
    color: '#fff',
    fontSize: 16,
    marginLeft: 12,
  },
  actions: {
    flexDirection: 'row',
    gap: 8,
  },
  actionButton: {
    backgroundColor: '#333',
    paddingHorizontal: 12,
    paddingVertical: 6,
    borderRadius: 4,
  },
  activeButton: {
    backgroundColor: '#6851D6',
  },
  actionText: {
    color: '#fff',
    fontSize: 12,
  },
  emptyText: {
    color: '#666',
    textAlign: 'center',
    padding: 20,
  },
});

export default ParticipantList;