Skip to content
Cloudflare Docs

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.

How the Waiting Room Works

After you call meeting.join(), one of two events will occur:

  • roomJoined - You are allowed to join the meeting immediately
  • waitlisted - 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.

Waiting Room States

State Flow

join()
[waitlisted] ←------ (host rejects)
↓ ↓
(host accepts) [rejected]
[joined]

Listening to State Changes

Joined Event

Triggered when the local user successfully joins the meeting.

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]);

Waitlisted Event

Triggered when the local user is placed in the waiting room.

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]);

Rejected Event

Triggered when the host rejects the entry request.

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]);

Monitor State with roomState

You can also directly check the current room state.

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;
}
}

Host Actions

Hosts can manage waiting room requests using participant management methods. See Remote Participants for details on:

  • acceptWaitingRoomRequest(participantId) - Accept a participant from the waiting room
  • rejectWaitingRoomRequest(participantId) - Reject a participant's entry request

Example: Host Accepting Participants

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>
);
}

Best Practices

  • 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