Remote Participants
This guide explains how to access participant data, display videos, handle events, and manage participant permissions in your RealtimeKit meetings.
The participant object contains all information related to a particular participants, including information about the grid and each participants media streams, name, and state variables. It is accessible via meeting.participants.
id- TheparticipantIdof the participant (akapeerId)userId- TheuserIdof the participantname- The participant's namepicture- The participant's picture (if any)customParticipantId- An arbitrary ID that can be set to identify the participantisPinned- Set totrueif the participant is pinnedpresetName- Name of the preset associated with the participant
id- Session-specific identifier generated when the participant joins meeting session (also known aspeerId)userId- Permanent identifier of the participant generated when adding the participant to a meetingname- Display name of the participantpicture- String URL to the participant's display picture (if any)customParticipantId- Custom identifier that can be set while adding participant to a meeting by customerisHost- Boolean value whether this participant has host privilegesisPinned- Whether this participant is currently pinned in the meetingpresetName- Name of the preset applied to this participant while adding to meetingstageStatus- Indicates the participant's current stage status (applicable only in stage-enabled meetings)
id- Session-specific identifier generated when the participant joins meeting session (also known aspeerId)userId- Permanent identifier of the participant generated when adding the participant to a meetingname- Display name of the participantpicture- String URL to the participant's display picture (if any)customParticipantId- Custom identifier that can be set while adding participant to a meeting by customerisHost- Boolean value whether this participant has host privilegesisPinned- Whether this participant is currently pinned in the meetingpresetName- Name of the preset applied to this participant while adding to meetingstageStatus- Indicates the participant's current stage status (applicable only in stage-enabled meetings)
id- Session-specific identifier generated when the participant joins meeting session (also known aspeerId)userId- Permanent identifier of the participant generated when adding the participant to a meetingname- Display name of the participantpicture- String URL to the participant's display picture (if any)isHost- Boolean value whether this participant has host privilegescustomParticipantId- Custom identifier that can be set while adding participant to a meeting by customerstageStatus- Indicates the participant's current stage status (applicable only in stage-enabled meetings)isPinned- Whether this participant is currently pinned in the meetingpresetName- Name of the preset applied to this participant while adding to meeting
videoEnabled- Set totrueif the participant's camera is onaudioEnabled- Set totrueif the participant is unmutedscreenShareEnabled- Set totrueif the participant is sharing their screenvideoTrack- The video track of the participantaudioTrack- The audio track of the participantscreenShareTracks- The video and audio tracks of the participant's screen share
// Number of participants joined in the meetingconsole.log(meeting.participants.count);
// Number of pages available in paginated modeconsole.log(meeting.participants.pageCount);
// Maximum number of participants in active stateconsole.log(meeting.participants.maxActiveParticipantsCount);
// ParticipantId of the last participant who spokeconsole.log(meeting.participants.lastActiveSpeaker);Use the useRealtimeKitSelector hook to access properties:
// Number of participants joined in the meetingconst participantCount = useRealtimeKitSelector((m) => m.participants.count);
// Number of pages available in paginated modeconst pageCount = useRealtimeKitSelector((m) => m.participants.pageCount);
// Maximum number of participants in active stateconst maxActiveCount = useRealtimeKitSelector( (m) => m.participants.maxActiveParticipantsCount,);
// ParticipantId of the last participant who spokeconst lastActiveSpeaker = useRealtimeKitSelector( (m) => m.participants.lastActiveSpeaker,);// Number of participants joined in the meetingval participantCount = meeting.participants.joined.size
// Access pagination propertiesval maxNumberOnScreen = meeting.participants.maxNumberOnScreenval currentPageNumber = meeting.participants.currentPageNumberval pageCount = meeting.participants.pageCountval canGoNextPage = meeting.participants.canGoNextPageval canGoPreviousPage = meeting.participants.canGoPreviousPage// Number of participants joined in the meetinglet participantCount = meeting.participants.joined.count
// Access pagination propertieslet maxNumberOnScreen = meeting.participants.maxNumberOnScreenlet currentPageNumber = meeting.participants.currentPageNumberlet pageCount = meeting.participants.pageCountlet canGoNextPage = meeting.participants.canGoNextPagelet canGoPreviousPage = meeting.participants.canGoPreviousPage// Number of participants joined in the meetingfinal participantCount = meeting.participants.joined.length;
// Access pagination propertiesfinal currentPageNumber = meeting.participants.currentPageNumber;final pageCount = meeting.participants.pageCount;final canGoNextPage = meeting.participants.isNextPagePossible;final canGoPreviousPage = meeting.participants.isPreviousPagePossible;Use the useRealtimeKitSelector hook to access properties:
// Number of participants joined in the meetingconst participantCount = useRealtimeKitSelector((m) => m.participants.count);
// Number of pages available in paginated modeconst pageCount = useRealtimeKitSelector((m) => m.participants.pageCount);
// Maximum number of participants in active stateconst maxActiveCount = useRealtimeKitSelector( (m) => m.participants.maxActiveParticipantsCount,);
// ParticipantId of the last participant who spokeconst lastActiveSpeaker = useRealtimeKitSelector( (m) => m.participants.lastActiveSpeaker,);You can fetch a participant from the participant maps.
const participant = meeting.participants.joined.get(participantId);
// Access participant propertiesconsole.log(participant.name);console.log(participant.videoEnabled);console.log(participant.audioEnabled);// Get a specific participantconst participant = useRealtimeKitSelector((m) => m.participants.joined.get(participantId),);
// Access participant propertiesconst participantName = participant?.name;const isVideoEnabled = participant?.videoEnabled;const isAudioEnabled = participant?.audioEnabled;// Find a participant by peer IDval participant = meeting.participants.joined.firstOrNull { it.id == participantId }
// Access participant propertiesparticipant?.let { println("Participant: ${it.name}") println("Video: ${it.videoEnabled}") println("Audio: ${it.audioEnabled}")}// Find a participant by peer IDif let participant = meeting.participants.joined.first(where: { $0.id == participantId }) { // Access participant properties print("Participant: \(participant.name)") print("Video: \(participant.videoEnabled)") print("Audio: \(participant.audioEnabled)")}// Find a participant by peer IDfinal participant = meeting.participants.joined .where((p) => p.id == "<peerId>") .firstOrNull;
// Access participant propertiesif (participant != null) { print('Participant: ${participant.name} (ID: ${participant.id})'); print('Audio: ${participant.audioEnabled ? "On" : "Off"}'); print('Video: ${participant.videoEnabled ? "On" : "Off"}');}// Get a specific participantconst participant = useRealtimeKitSelector((m) => m.participants.joined.get(participantId),);
// Access participant propertiesconst participantName = participant?.name;const isVideoEnabled = participant?.videoEnabled;const isAudioEnabled = participant?.audioEnabled;All participants are stored under meeting.participants. These do not include the local user.
The meeting.participants object contains the following maps:
joined- All participants currently in the meeting (excluding the local user)waitlisted- All participants waiting to join the meetingactive- All participants whose media is subscribed to (participants that should be displayed on screen)pinned- All pinned participants in the meeting
If you are building a video/audio grid, use the active map. To display a list of all participants, use the joined map.
Each participant in these maps is of type RTKParticipant.
All participants are stored under meeting.participants. These do not include the local user.
The meeting.participants object contains the following lists:
joined- All participants currently in the meeting (excluding the local user)waitlisted- All participants waiting to join the meetingactive- All participants whose media is subscribed to (participants that should be displayed on screen)pinned- All pinned participants in the meetingscreenShares- All participants who are sharing their screen
If you are building a video/audio grid, use the active list. To display a list of all participants, use the joined list.
All participants are stored under meeting.participants. These do not include the local user.
The meeting.participants object contains the following lists:
joined- All participants currently in the meeting (excluding the local user)waitlisted- All participants waiting to join the meetingactive- All participants whose media is subscribed to (participants that should be displayed on screen)pinned- All pinned participants in the meeting
If you are building a video/audio grid, use the active list. To display a list of all participants, use the joined list.
Each participant in these lists is of type RtkRemoteParticipant.
// Get all joined participantsconst joinedParticipants = meeting.participants.joined;
// Get active participants (those on screen)const activeParticipants = meeting.participants.active;
// Get pinned participantsconst pinnedParticipants = meeting.participants.pinned;
// Get waitlisted participantsconst waitlistedParticipants = meeting.participants.waitlisted;Use the useRealtimeKitSelector hook to access participant maps:
import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react";
// Get all joined participantsconst joinedParticipants = useRealtimeKitSelector((m) => m.participants.joined);
// Get active participants (those on screen)const activeParticipants = useRealtimeKitSelector((m) => m.participants.active);
// Get pinned participantsconst pinnedParticipants = useRealtimeKitSelector((m) => m.participants.pinned);
// Get waitlisted participantsconst waitlistedParticipants = useRealtimeKitSelector( (m) => m.participants.waitlisted,);// Get all joined participantsval joinedParticipants: List<RtkRemoteParticipant> = meeting.participants.joined
// Get active participants (those on screen)val activeParticipants: List<RtkRemoteParticipant> = meeting.participants.active
// Get pinned participantsval pinnedParticipants: List<RtkRemoteParticipant> = meeting.participants.pinned
// Get waitlisted participantsval waitlistedParticipants: List<RtkRemoteParticipant> = meeting.participants.waitlisted
// Get screen sharing participantsval screenShareParticipants: List<RtkRemoteParticipant> = meeting.participants.screenShares// Get all joined participantslet joinedParticipants: [RtkRemoteParticipant] = meeting.participants.joined
// Get active participants (those on screen)let activeParticipants: [RtkRemoteParticipant] = meeting.participants.active
// Get pinned participantslet pinnedParticipants: [RtkRemoteParticipant] = meeting.participants.pinned
// Get waitlisted participantslet waitlistedParticipants: [RtkRemoteParticipant] = meeting.participants.waitlisted
// Get screen sharing participantslet screenShareParticipants: [RtkRemoteParticipant] = meeting.participants.screenShares// Get all joined participantsfinal joinedParticipants = meeting.participants.joined;
// Get active participants (those on screen)final activeParticipants = meeting.participants.active;
// Get pinned participantsfinal pinnedParticipants = meeting.participants.pinned;
// Get waitlisted participantsfinal waitlistedParticipants = meeting.participants.waitlisted;Use the useRealtimeKitSelector hook to access participant maps:
import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react-native";
// Get all joined participantsconst joinedParticipants = useRealtimeKitSelector((m) => m.participants.joined);
// Get active participants (those on screen)const activeParticipants = useRealtimeKitSelector((m) => m.participants.active);
// Get pinned participantsconst pinnedParticipants = useRealtimeKitSelector((m) => m.participants.pinned);
// Get waitlisted participantsconst waitlistedParticipants = useRealtimeKitSelector( (m) => m.participants.waitlisted,);The view mode indicates whether participants are populated in ACTIVE_GRID mode or PAGINATED mode.
ACTIVE_GRIDmode - Participants are automatically replaced inmeeting.participants.activebased on who is speaking or who has their video turned onPAGINATEDmode - Participants inmeeting.participants.activeare fixed. UsesetPage()to change the active participants
// Set the view mode to paginatedawait meeting.participants.setViewMode("PAGINATED");
// Set the view mode to active gridawait meeting.participants.setViewMode("ACTIVE_GRID");Use the useRealtimeKitClient hook to access the meeting object:
import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";
const [meeting] = useRealtimeKitClient();
// Set the view mode to paginatedawait meeting.participants.setViewMode("PAGINATED");
// Set the view mode to active gridawait meeting.participants.setViewMode("ACTIVE_GRID");Android SDK uses active grid mode by default on page 0. If you switch to the next page, it automatically switches to paginated mode.
iOS SDK uses active grid mode by default on page 0. If you switch to the next page, it automatically switches to paginated mode.
Flutter SDK uses active grid mode by default on page 0. If you switch to the next page, it automatically switches to paginated mode.
// Set the view mode to paginatedawait meeting.participants.setViewMode("PAGINATED");
// Set the view mode to active gridawait meeting.participants.setViewMode("ACTIVE_GRID");// Switch to second pageawait meeting.participants.setPage(2);import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";
const [meeting] = useRealtimeKitClient();
// Switch to second pageawait meeting.participants.setPage(2);// Switch to first pagemeeting.participants.setPage(1)// Switch to first pagemeeting.participants.setPage(1)Flutter SDK automatically manages participant pagination.
// Switch to second pageawait meeting.participants.setPage(2);const viewMode = meeting.participants.viewMode;const currentPage = meeting.participants.currentPage;const viewMode = useRealtimeKitSelector((m) => m.participants.viewMode);const currentPage = useRealtimeKitSelector((m) => m.participants.currentPage);Monitoring view mode is not available on this platform.
const viewMode = useRealtimeKitSelector((m) => m.participants.viewMode);const currentPage = useRealtimeKitSelector((m) => m.participants.currentPage);The participant object allows the host several controls. These can be selected while creating the host preset.
With the correct permissions, the host can disable media for remote participants.
const participant = meeting.participants.joined.get(participantId);
// Disable a participant's video streamparticipant.disableVideo();
// Disable a participant's audio streamparticipant.disableAudio();
// Kick a participant from the meetingparticipant.kick();import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";
const [meeting] = useRealtimeKitClient();const participant = meeting.participants.joined.get(participantId);
// Disable a participant's video streamparticipant.disableVideo();
// Disable a participant's audio streamparticipant.disableAudio();
// Kick a participant from the meetingparticipant.kick();val participant = meeting.participants.joined.firstOrNull { it.id == participantId }
participant?.let { pcpt -> // Disable a participant's video stream val videoError = pcpt.disableVideo()
// Disable a participant's audio stream val audioError = pcpt.disableAudio()
// Kick a participant from the meeting val kickError = pcpt.kick()}if let participant = meeting.participants.joined.first(where: { $0.id == participantId }) { // Disable a participant's video stream let videoError: HostError? = participant.disableVideo()
// Disable a participant's audio stream let audioError: HostError? = participant.disableAudio()
// Kick a participant from the meeting let kickError: HostError? = participant.kick()}// Disable a remote participant's videoparticipant.disableVideo(onResult: (e) { // handle error if any});
// Disable a remote participant's audioparticipant.disableAudio(onResult: (e) { // handle error if any});
// Remove the participant from the meetingparticipant.kick();Required Permission: permissions.host.canDisableVideo, permissions.host.canDisableAudio must be true
const participant = meeting.participants.joined.get(participantId);
// Disable a participant's video streamparticipant.disableVideo();
// Disable a participant's audio streamparticipant.disableAudio();
// Kick a participant from the meetingparticipant.kick();The waiting room allows the host to control which users can join your meeting and when. They can either choose to accept or reject the request.
You can also automate this flow so that users join the meeting automatically when the host joins the meeting, using presets.
await meeting.participants.acceptWaitingRoomRequest(participantId);import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";
const [meeting] = useRealtimeKitClient();
await meeting.participants.acceptWaitingRoomRequest(participantId);meeting.participants.acceptWaitingRoomRequest(participantId)meeting.participants.acceptWaitingRoomRequest(id: participantId)final participant = meeting.participants.waitlisted[0];meeting.participants.acceptWaitlistedParticipant(participant);await meeting.participants.acceptWaitingRoomRequest(participantId);await meeting.participants.rejectWaitingRoomRequest(participantId);import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";
const [meeting] = useRealtimeKitClient();
await meeting.participants.rejectWaitingRoomRequest(participantId);meeting.participants.rejectWaitingRoomRequest(participantId)meeting.participants.rejectWaitingRoomRequest(participantId)final participant = meeting.participants.waitlisted[0];meeting.participants.rejectWaitlistedParticipant(participant);await meeting.participants.rejectWaitingRoomRequest(participantId);The host can choose to pin or unpin participants to the grid.
const participant = meeting.participants.joined.get(participantId);
// Pin a participantawait participant.pin();
// Unpin a participantawait participant.unpin();import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";
const [meeting] = useRealtimeKitClient();const participant = meeting.participants.joined.get(participantId);
// Pin a participantawait participant.pin();
// Unpin a participantawait participant.unpin();val participant = meeting.participants.joined.firstOrNull { it.id == participantId }
participant?.let { pcpt -> // Pin a participant val pinError = pcpt.pin()
// Unpin a participant val unpinError = pcpt.unpin()}if let participant = meeting.participants.joined.first(where: { $0.id == participantId }) { // Pin a participant let pinError: HostError? = participant.pin()
// Unpin a participant let unpinError: HostError? = participant.unpin()}// Pin a remote participantparticipant.pin();
// Unpin a previously pinned participantparticipant.unpin();Required Permission: permissions.host.canPinParticipant must be true
const participant = meeting.participants.joined.get(participantId);
// Pin a participantawait participant.pin();
// Unpin a participantawait participant.unpin();The host can modify the permissions for a participant. Permissions for a participant are defined by their preset.
Updating participant permissions is not available on this platform.
First, find the participant(s) you want to update.
const participantIds = meeting.participants.joined .toArray() .filter((e) => e.name.startsWith("John")) .map((p) => p.id);Use the updatePermissions method to modify the permissions for the participant.
// Allow file upload permissions in public chatconst newPermissions = { chat: { public: { files: true, }, },};
meeting.participants.updatePermissions(participantIds, newPermissions);The following permissions can be modified:
interface UpdatedPermissions { polls?: { canCreate?: boolean; canVote?: boolean; }; plugins?: { canClose?: boolean; canStart?: boolean; }; chat?: { public?: { canSend?: boolean; text?: boolean; files?: boolean; }; private?: { canSend?: boolean; text?: boolean; files?: boolean; }; };}To play a participant's video track on a <video> element:
<video class="participant-video" id="participant-video"></video>// Get the video elementconst videoElement = document.getElementById("participant-video");
// Get the participantconst participant = meeting.participants.joined.get(participantId);
// Register the video elementparticipant.registerVideoElement(videoElement);For local user preview (video not sent to other users):
meeting.self.registerVideoElement(videoElement, true);Clean up when the video element is no longer needed:
participant.deregisterVideoElement(videoElement);To play a participant's video track on a <video> element:
import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";
const [meeting] = useRealtimeKitClient();
// Get the video elementconst videoElement = document.getElementById("participant-video");
// Get the participantconst participant = meeting.participants.joined.get(participantId);
// Register the video elementparticipant.registerVideoElement(videoElement);
// Clean up when the video element is no longer neededparticipant.deregisterVideoElement(videoElement);For local user preview (video not sent to other users):
meeting.self.registerVideoElement(videoElement, true);Call participant.getVideoView() which returns a View that renders the participant's video stream:
// Get video view of a given participantval videoView = participant.getVideoView()
// Get screen share video viewval screenShareView = participant.getScreenShareVideoView()Call participant.getVideoView() which returns a UIView that renders the participant's video stream:
// Get video view of a given participantlet videoView = participant.getVideoView()
// Get screen share video viewlet screenShareView = participant.getScreenShareVideoView()Use the video view methods which return a Widget that you can place directly in your UI hierarchy:
// Create a widget to display the participant's camera videofinal cameraView = VideoView(meetingParticipant: participant);
// Create a widget to display the participant's screen sharefinal screenShareView = ScreenshareView(meetingParticipant: participant);Use useRealtimeKitSelector to get the video track and render it with RTCView:
import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react-native";import { MediaStream, RTCView } from "@cloudflare/react-native-webrtc";
function VideoView() { const { videoTrack } = useRealtimeKitSelector((m) => m.participants.active.toArray(), )[0];
const stream = new MediaStream(undefined); stream.addTrack(videoTrack);
return ( <RTCView objectFit="cover" style={{ flex: 1 }} streamURL={stream.toURL()} mirror={true} zOrder={1} /> );}Was this helpful?
- Resources
- API
- New to Cloudflare?
- Directory
- Sponsorships
- Open Source
- Support
- Help Center
- System Status
- Compliance
- GDPR
- Company
- cloudflare.com
- Our team
- Careers
- © 2026 Cloudflare, Inc.
- Privacy Policy
- Terms of Use
- Report Security Issues
- Trademark
-