Invite Guest on Stage - React Native
This guide explains the process of inviting a viewer to join a livestream using the changeMode() method.
This page uses the following asynchronous methods. Refer to their API reference for the errors each Promise may reject with, and handle these rejections appropriately based on your use case.
Before proceeding with this guide, ensure that all attendees join the room with the mode set to SIGNALLING_ONLY, while the host joins with the mode set to SEND_AND_RECV.
The diagram below illustrates the utilization of the PubSub mechanism to facilitate the requesting and switching of the participant's mode.

Step 1: Loading the Viewer List
First, the host will be presented with a list of participants who have joined as SIGNALLING_ONLY, accompanied by a button that, when selected, will invite that user to join the livestream.
Each list item reads the participant's mode from the useParticipant hook, which updates when the participant changes mode. A viewer who switches to SEND_AND_RECV leaves the list.
import {
Constants,
useRoom,
useParticipant,
} from "@videosdk.live/react-native-sdk";
import { SafeAreaView, TouchableOpacity, Text, View } from "react-native";
function RoomView() {
return (
<>
<Text>Viewer List</Text>
<ViewerList />
</>
);
}
function ViewerList() {
const { participants } = useRoom();
return (
<SafeAreaView style={{ flex: 1 }}>
{[...participants.keys()].map((participantId) => {
return (
<ViewerListItem key={participantId} participantId={participantId} />
);
})}
</SafeAreaView>
);
}
function ViewerListItem({ participantId }) {
const { displayName, mode } = useParticipant(participantId);
//Showing only viewer participants
if (mode !== Constants.Mode.SIGNALLING_ONLY) {
return null;
}
return (
<View
style={{
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
marginVertical: 8,
}}
>
<Text>{displayName}</Text>
<TouchableOpacity
style={{
marginLeft: 12,
borderWidth: 1,
borderRadius: 12,
padding: 12,
}}
>
<Text>Request to join Livestream</Text>
</TouchableOpacity>
</View>
);
}
Step 2: Requesting a Viewer to Join Livestream
Now, with the Viewer list in place, handle the click event for the "Request to join Livestream" button by utilizing CHANGE_MODE_$participantId as the topic for PubSub.
import {
Constants,
usePubSub,
useParticipant,
} from "@videosdk.live/react-native-sdk";
import { TouchableOpacity, Text, View } from "react-native";
function ViewerListItem({ participantId }) {
const { displayName, mode } = useParticipant(participantId);
const { publish } = usePubSub(`CHANGE_MODE_${participantId}`);
//Publishing the pubsub message with new mode
const onClickRequestJoinLiveStream = async () => {
try {
await publish(
JSON.stringify({
mode: "SEND_AND_RECV",
})
);
} catch (error) {
console.log("Error while publishing:", error);
}
};
if (mode !== Constants.Mode.SIGNALLING_ONLY) {
return null;
}
return (
<View
style={{
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
marginVertical: 8,
}}
>
<Text>{displayName}</Text>
<TouchableOpacity
onPress={() => {
onClickRequestJoinLiveStream();
}}
style={{
marginLeft: 12,
borderWidth: 1,
borderRadius: 12,
padding: 12,
}}
>
<Text>Request to join Livestream</Text>
</TouchableOpacity>
</View>
);
}
Step 3: Showing Viewer Request Dialog
After implementing the host's request for a viewer to join the livestream, display the viewer's request dialogue and switch the mode from SIGNALLING_ONLY to SEND_AND_RECV.
On Android, the SDK does not ask for camera and microphone access. So the dialog's Accept handler asks for it with requestPermission() from the useMediaDevice hook before it switches to SEND_AND_RECV.
import {
Constants,
useMediaDevice,
useRoom,
usePubSub,
} from "@videosdk.live/react-native-sdk";
import Video from "react-native-video";
import { Alert, SafeAreaView, Text } from "react-native";
function ViewerView({}) {
const { hlsState, hlsUrls, localParticipant, changeMode } = useRoom();
const { requestPermission } = useMediaDevice();
usePubSub(`CHANGE_MODE_${localParticipant?.id}`, {
onMessageReceived: (data) => {
const { message, senderName } = data;
const parsed = JSON.parse(message);
if (parsed.mode === "SEND_AND_RECV") {
showAlert(senderName);
}
},
});
const showAlert = (senderName) => {
Alert.alert(
"Permission",
`${senderName} has requested you to join as a speaker`,
[
{
text: "Reject",
onPress: () => console.log("Cancel Pressed"),
style: "cancel",
},
{
text: "Accept",
onPress: async () => {
try {
// Ask for camera and microphone access before going on stage
await requestPermission(Constants.permission.AUDIO_AND_VIDEO);
await changeMode("SEND_AND_RECV");
} catch (err) {
console.error("changeMode failed:", err);
}
},
},
]
);
};
return (
<SafeAreaView style={{ flex: 1 }}>
{hlsState == "HLS_PLAYABLE" ? (
<>
{/* Render VideoPlayer that will play `playbackHlsUrl`*/}
<Video
controls={true}
source={{
uri: hlsUrls.playbackHlsUrl,
}}
resizeMode={"stretch"}
style={{
flex: 1,
backgroundColor: "black",
}}
onError={(e) => console.log("error", e)}
/>
</>
) : (
<SafeAreaView
style={{ flex: 1, justifyContent: "center", alignItems: "center" }}
>
<Text style={{ fontSize: 20 }}>
HLS is not started yet or is stopped
</Text>
</SafeAreaView>
)}
</SafeAreaView>
);
}
Step 4: Pin the participant
To ensure the participant appears on the livestream, they have to be pinned. This can be achieved by listening to the callback on the onParticipantModeChanged event of the useRoom hook.
import { useRoom } from "@videosdk.live/react-native-sdk";
import { useEffect, useRef } from "react";
function RoomView() {
const mRoom = useRoom({});
///Here a reference to the room value is used because
//while referencing it in the callbacks its latest state has to be obtained.
const mRoomRef = useRef(mRoom);
useEffect(() => {
mRoomRef.current = mRoom;
}, [mRoom]);
useRoom({
onParticipantModeChanged: async ({ participantId, mode }) => {
const localParticipant = mRoomRef.current?.localParticipant;
if (participantId === localParticipant?.id) {
if (mode === "SEND_AND_RECV") {
try {
await localParticipant.pin("SHARE_AND_CAM");
} catch (err) {
console.error("pin failed:", err);
}
} else {
try {
await localParticipant.unpin("SHARE_AND_CAM");
} catch (err) {
console.error("unpin failed:", err);
}
}
}
},
});
return <>...</>;
}
For the complete source code, you can checkout our code sample. The sample still uses React Native SDK v1.0.1. The Migration Guide lists what changed in v2.0.0.
API Reference
The API references for all the methods utilized in this guide are provided below.
Got a Question? Ask us on discord

