Waiting Room
The waiting room feature allows hosts to control who can join a meeting. When enabled, participants must wait for approval before entering the meeting.
After you call meeting.join(), one of two events will occur:
roomJoined- You are allowed to join the meeting immediatelywaitlisted- You are placed in the waiting room and must wait for host approval
Use meeting.self.roomState to track the user's state in the meeting.
join() ↓ [waitlisted] ←------ (host rejects) ↓ ↓ (host accepts) [rejected] ↓ [joined]Triggered when the local user successfully joins the meeting:
meeting.self.on("roomJoined", () => { // Local user is in the meeting console.log("Successfully joined the meeting");});Triggered when the local user is placed in the waiting room:
meeting.self.on("waitlisted", () => { // Local user is waitlisted console.log("You are in the waiting room. Waiting for host approval...");});Triggered when the host rejects the entry request:
meeting.self.on("roomLeft", ({ state }) => { if (state === "rejected") { // Host rejected the entry console.log("Your entry request was rejected"); }});You can also directly check the current room state:
const currentState = meeting.self.roomState;
if (currentState === "waitlisted") { console.log("Waiting for approval");} else if (currentState === "joined") { console.log("In the meeting");} else if (currentState === "rejected") { console.log("Entry was rejected");}Monitor when the local user joins the meeting:
import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react";import { useEffect } from "react";
function MeetingStatus() { const roomState = useRealtimeKitSelector((m) => m.self.roomState); const joined = roomState === "joined";
useEffect(() => { if (joined) { console.log("Successfully joined the meeting"); } }, [joined]);
return joined ? <div>You are in the meeting</div> : null;}Alternatively, use event listeners:
import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";
useEffect(() => { if (!meeting) return;
const handleRoomJoined = () => { console.log("Successfully joined the meeting"); };
meeting.self.on("roomJoined", handleRoomJoined);
return () => { meeting.self.off("roomJoined", handleRoomJoined); };}, [meeting]);Monitor when the local user is in the waiting room:
function WaitingRoomStatus() { const roomState = useRealtimeKitSelector((m) => m.self.roomState); const isWaitlisted = roomState === "waitlisted";
useEffect(() => { if (isWaitlisted) { console.log("You are in the waiting room"); } }, [isWaitlisted]);
return isWaitlisted ? <div>Waiting for host approval...</div> : null;}Alternatively, use event listeners:
useEffect(() => { if (!meeting) return;
const handleWaitlisted = () => { console.log("You are in the waiting room"); };
meeting.self.on("waitlisted", handleWaitlisted);
return () => { meeting.self.off("waitlisted", handleWaitlisted); };}, [meeting]);Monitor when the host rejects the entry request:
function RejectionStatus() { const roomState = useRealtimeKitSelector((m) => m.self.roomState); const rejected = roomState === "rejected";
useEffect(() => { if (rejected) { console.log("Your entry request was rejected"); } }, [rejected]);
return rejected ? <div>Your entry was rejected by the host</div> : null;}Alternatively, use event listeners:
useEffect(() => { if (!meeting) return;
const handleRoomLeft = ({ state }) => { if (state === "rejected") { console.log("Your entry request was rejected"); } };
meeting.self.on("roomLeft", handleRoomLeft);
return () => { meeting.self.off("roomLeft", handleRoomLeft); };}, [meeting]);Handle all waiting room states in one component:
function WaitingRoomManager() { const roomState = useRealtimeKitSelector((m) => m.self.roomState);
switch (roomState) { case "init": return <div>Connecting...</div>; case "waitlisted": return <div>Waiting for host approval...</div>; case "joined": return <div>You are in the meeting</div>; case "rejected": return <div>Your entry was rejected</div>; case "left": return <div>You left the meeting</div>; case "kicked": return <div>You were removed from the meeting</div>; case "ended": return <div>The meeting has ended</div>; case "disconnected": return <div>Connection lost</div>; default: return null; }}Hosts can manage waiting room requests using participant management methods. See Remote Participants for details on:
acceptWaitingRoomRequest(participantId)- Accept a participant from the waiting roomrejectWaitingRoomRequest(participantId)- Reject a participant's entry request
// Get waitlisted participantsconst waitlistedParticipants = meeting.participants.waitlisted.toArray();
// Accept the first waitlisted participantif (waitlistedParticipants.length > 0) { const participantId = waitlistedParticipants[0].id; await meeting.participants.acceptWaitingRoomRequest(participantId);}import { useRealtimeKitClient, useRealtimeKitSelector,} from "@cloudflare/realtimekit-react";
function WaitingRoomHost() { const [meeting] = useRealtimeKitClient(); const waitlistedParticipants = useRealtimeKitSelector((m) => m.participants.waitlisted.toArray(), );
const acceptParticipant = async (participantId) => { await meeting.participants.acceptWaitingRoomRequest(participantId); };
const rejectParticipant = async (participantId) => { await meeting.participants.rejectWaitingRoomRequest(participantId); };
return ( <div> <h3>Waiting Room ({waitlistedParticipants.length})</h3> {waitlistedParticipants.map((participant) => ( <div key={participant.id}> <span>{participant.name}</span> <button onClick={() => acceptParticipant(participant.id)}> Accept </button> <button onClick={() => rejectParticipant(participant.id)}> Reject </button> </div> ))} </div> );}- Provide Clear Feedback - Show users when they're in the waiting room and that they're waiting for approval
- Set Expectations - Let users know their request is being reviewed
- Handle Rejection Gracefully - Provide a friendly message if entry is rejected
- Monitor State Changes - Subscribe to room state changes to update your UI accordingly
- Check Permissions - Ensure your app has appropriate permissions configured in the preset to use waiting room features
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
- © 2025 Cloudflare, Inc.
- Privacy Policy
- Terms of Use
- Report Security Issues
- Trademark
-