CoGuestState module, purpose-built to handle the entire workflow of audience co-hosting in live streaming scenarios. You can easily enable robust audio/video interactive features between hosts and audience members with just a few method calls—no need to manage complex state synchronization or signaling logic.CoGuestState supports the two most common co-hosting scenarios:applyForSeat method.import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';const liveID = 'xxx'; // The liveID of the current live room// Get the CoGuestState instance using liveIDconst { applyForSeat } = useCoGuestState(liveID);// User taps "Request to Co-host"const handleRequestToConnect = () => {applyForSeat({liveID,seatIndex: -1, // Use -1 for random seat assignmenttimeout: 30, // Timeout in seconds (e.g., 30s)});};
onGuestApplicationResponded event using addCoGuestGuestListener to get the host’s decision.import { useEffect } from 'react';import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';import { useDeviceState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/DeviceState';const liveID = 'xxx'; // The liveID of the current live roomconst { addCoGuestGuestListener, removeCoGuestGuestListener } = useCoGuestState(liveID);const { openLocalMicrophone, openLocalCamera } = useDeviceState(liveID);// Subscribe to event on page initializationconst handleGuestApplicationResponded = (event) => {if (event.isAccept) {console.log('Co-host request accepted');openLocalMicrophone();openLocalCamera({ isFront: true });// Update UI to reflect co-hosting status} else {console.log('Co-host request rejected');// Display a dialog notifying the user of rejection}};useEffect(() => {addCoGuestGuestListener('onGuestApplicationResponded', handleGuestApplicationResponded);return () => {removeCoGuestGuestListener('onGuestApplicationResponded', handleGuestApplicationResponded);};}, []);
disconnect method to return to normal audience status.import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';const liveID = 'xxx'; // The liveID of the current live roomconst { disconnect } = useCoGuestState(liveID);// User taps "Leave Co-host Seat"const handleDisconnect = () => {disconnect({liveID,onSuccess: () => { console.log('Left co-host seat successfully'); },onError: (error) => { console.log('Failed to leave co-host seat', error); },});};
cancelApplication.import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';const liveID = 'xxx'; // The liveID of the current live roomconst { cancelApplication } = useCoGuestState(liveID);// User taps "Cancel Request" while waitingconst handleCancelRequest = () => {cancelApplication({liveID,onSuccess: () => { console.log('Request cancelled successfully'); },onError: (error) => { console.log('Failed to cancel request', error); },});};
onGuestApplicationReceived event with CoGuestHostListener to get notified when a new audience request arrives.import { useEffect } from 'react';import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';const liveID = 'xxx'; // The liveID of your current live roomconst { addCoGuestHostListener, removeCoGuestHostListener } = useCoGuestState(liveID);// Subscribe to event on page initializationconst handleGuestApplicationReceived = (event) => {console.log('Received audience co-host request', event);// Update UI, e.g., show notification indicator on request list button};useEffect(() => {addCoGuestHostListener('onGuestApplicationReceived', handleGuestApplicationReceived);return () => {removeCoGuestHostListener('onGuestApplicationReceived', handleGuestApplicationReceived);};}, []);
CoGuestState provides a real-time list of applicants, which can be used directly in your UI.import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';const liveID = 'xxx'; // The liveID of your current live roomconst { applicants } = useCoGuestState(liveID);// Render your applicant list UI<FlatListdata={applicants || []}keyExtractor={(item) => item.userID}renderItem={({ item }) => (<View><Text>{item.userName}</Text><Text>{item.userID}</Text></View>)}/>
import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';const liveID = 'xxx'; // The liveID of your current live roomconst { acceptApplication, rejectApplication } = useCoGuestState(liveID);// Host clicks "Accept" button for a userconst handleAccept = (userID) => {acceptApplication({liveID,userID,onSuccess: () => { console.log('Accepted co-host request successfully'); },onError: (error) => { console.log('Failed to accept co-host request', error); },});};// Host clicks "Reject" button for a userconst handleReject = (userID) => {rejectApplication({liveID,userID,onSuccess: () => { console.log('Rejected co-host request successfully'); },onError: (error) => { console.log('Failed to reject co-host request', error); },});};
inviteToSeat method.import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';const liveID = 'xxx'; // The liveID of your current live roomconst { inviteToSeat } = useCoGuestState(liveID);// Host selects an audience member and sends an inviteconst handleInviteToSeat = (inviteeID) => {inviteToSeat({liveID,inviteeID,seatIndex: -1, // Use -1 for random seat assignmenttimeout: 30, // Timeout duration in secondsonSuccess: () => { console.log('Sent co-host invitation to audience'); },onError: (error) => { console.log('Failed to send co-host invitation', error); },});};
onHostInvitationResponded event using CoGuestHostListener.import { useEffect } from 'react';import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';const liveID = 'xxx'; // The liveID of your current live roomconst { addCoGuestHostListener, removeCoGuestHostListener } = useCoGuestState(liveID);// Subscribe to event on page initializationconst handleHostInvitationResponded = (event) => {if (event.isAccept) {console.log('Audience accepted your invitation');} else {console.log('Audience rejected your invitation');}};useEffect(() => {addCoGuestHostListener('onHostInvitationResponded', handleHostInvitationResponded);return () => {removeCoGuestHostListener('onHostInvitationResponded', handleHostInvitationResponded);};}, []);
onHostInvitationReceived event using addCoGuestGuestListener.import { useEffect } from 'react';import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';import { useLiveListState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/LiveListState';const liveID = 'xxx'; // The liveID of the current live roomconst { addCoGuestGuestListener, removeCoGuestGuestListener } = useCoGuestState(liveID);const { currentLive } = useLiveListState();// Subscribe to event on page initializationconst handleHostInvitationReceived = (event) => {console.log('Received host invitation to co-host', event);const inviterID = currentLive?.liveOwner?.userID || '';// Display a dialog for the user to accept or reject};useEffect(() => {addCoGuestGuestListener('onHostInvitationReceived', handleHostInvitationReceived);return () => {removeCoGuestGuestListener('onHostInvitationReceived', handleHostInvitationReceived);};}, [currentLive]);
import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';import { useDeviceState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/DeviceState';import { useLiveListState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/LiveListState';const liveID = 'xxx'; // The liveID of the current live roomconst { acceptInvitation, rejectInvitation } = useCoGuestState(liveID);const { openLocalMicrophone, openLocalCamera } = useDeviceState(liveID);const { currentLive } = useLiveListState();// Get inviterID from currentLiveconst inviterID = currentLive?.liveOwner?.userID || '';// User clicks "Accept"const handleAcceptInvitation = () => {acceptInvitation({liveID,inviterID,onSuccess: () => {console.log('Accepted invitation successfully');openLocalMicrophone();openLocalCamera({ isFront: true });},onError: (error) => { console.log('Failed to accept invitation', error); },});};// User clicks "Reject"const handleRejectInvitation = () => {rejectInvitation({liveID,inviterID,onSuccess: () => { console.log('Rejected invitation successfully'); },onError: (error) => { console.log('Failed to reject invitation', error); },});};


import React, { useMemo } from 'react';import { View, Text, Image, StyleSheet, Dimensions } from 'react-native';const DEFAULT_AVATAR = 'https://liteav-test-1252463788.cos.ap-guangzhou.myqcloud.com/voice_room/voice_room_cover1.png';const { width: SCREEN_WIDTH } = Dimensions.get('window');// Receives seatList and canvas from parent componentexport default function ParticipantOverlay({ seatList, canvas }) {if (!seatList || seatList.length === 0) return null;// Calculate scaling ratio based on canvasconst scale = useMemo(() => {if (!canvas?.w || !canvas?.h) return { scaleX: 1, scaleY: 1 };const displayWidth = SCREEN_WIDTH;const displayHeight = SCREEN_WIDTH * (canvas.h / canvas.w);return {scaleX: displayWidth / canvas.w,scaleY: displayHeight / canvas.h,};}, [canvas]);// Calculate precise position and size for each participant's UI containerconst getParticipantStyle = (participant) => {if (!participant?.region) return {};return {position: 'absolute',left: participant.region.x * scale.scaleX,top: participant.region.y * scale.scaleY,width: participant.region.w * scale.scaleX,height: participant.region.h * scale.scaleY,};};return (// overlay-container<View style={styles.overlayContainer} pointerEvents="none">{/* Iterate seatList to create a UI container for each co-host participant */}{seatList.map((participant) => {if (!participant?.userInfo?.userID) return null;const isCameraOff = participant.userInfo.cameraStatus === 'OFF';return (// participant-ui-container<View key={participant.userInfo.userID} style={getParticipantStyle(participant)}>{/* Conditional rendering: show different UI based on camera status */}{isCameraOff ? (// When camera is off, show avatar and nickname centered<View style={styles.avatarPlaceholder}><Imagestyle={styles.avatarImage}source={{ uri: participant.userInfo.userAvatar || DEFAULT_AVATAR }}/><Text style={styles.avatarName}>{participant.userInfo.userName || participant.userInfo.userID}</Text></View>) : (// When camera is on, show nickname bar at bottom left<View style={styles.nicknameBar}><Text style={styles.nicknameText}>{participant.userInfo.userName || participant.userInfo.userID}</Text></View>)}</View>);})}</View>);}const styles = StyleSheet.create({overlayContainer: {position: 'absolute',top: 0,left: 0,right: 0,bottom: 0,},avatarPlaceholder: {flex: 1,backgroundColor: '#2E323A',justifyContent: 'center',alignItems: 'center',},avatarImage: {width: 60,height: 60,borderRadius: 30,},avatarName: {marginTop: 8,fontSize: 13,color: '#fff',},nicknameBar: {position: 'absolute',left: 6,bottom: 6,backgroundColor: 'rgba(0, 0, 0, 0.5)',paddingHorizontal: 8,paddingVertical: 3,borderRadius: 10,},nicknameText: {color: '#fff',fontSize: 11,},});
import React from 'react';import { StyleSheet, View } from 'react-native';import { LiveCoreView } from 'react-native-tuikit-atomic-x/lib/module/components/LiveCoreView';import { useLiveSeatState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/LiveSeatState';import ParticipantOverlay from '../../components/ParticipantOverlay';export default function YourAnchorScreen({ route, navigation }) {const { liveID } = route.params || {};const { seatList, canvas } = useLiveSeatState(liveID);return (// page-container<View style={styles.pageContainer}>{/* live-container */}<View style={styles.liveContainer}>{/* Video rendering layer */}<LiveCoreViewliveID={liveID}coreViewType="pushView" // Host: pushView, Audience: playViewstyle={styles.videoLayer}/>{/* UI overlay layer */}<ParticipantOverlay seatList={seatList} canvas={canvas} /></View>{/* Additional UI elements, such as the bottom control bar */}{/* <View style={styles.bottomControls}>...</View> */}</View>);}const styles = StyleSheet.create({pageContainer: {flex: 1,backgroundColor: '#000',},liveContainer: {flex: 1,},videoLayer: {flex: 1,},});
State | Description | API Documentation |
DeviceState | Controls audio/video devices: microphone (on/off/volume), camera (on/off/switch/quality), screen sharing, and real-time device status. | |
CoGuestState | Manages audience co-hosting: requests, invitations, accept/reject actions, member permissions (microphone/camera), and state sync. | |
LiveSeatState | Manages seat information: seat list and seat order. |
ParticipantOverlay component as described in "Enhancing UI Details," the nickname bar or avatar placeholder does not align correctly with the video window, causing offset or size mismatches.participant.region.x * scale.scaleX. If the scaling ratio is wrong, the UI will be misaligned.ParticipantOverlay must receive the canvas object (server-side canvas dimensions) from useLiveSeatState(liveID). The component will calculate the scaling ratio automatically: scaleX = screen width / canvas.w.CoGuestState methods such as applyForSeat, acceptApplication, inviteToSeat, etc., no actions occur and callbacks are not received.CoGuestState features are tied to a specific live room. The most frequent issue is an incorrect, null, or mismatched liveID when initializing or invoking CoGuestState.liveID: When using useCoGuestState(liveID) and related methods (e.g., applyForSeat({ liveID, ... })), ensure the liveID matches the actual live room.liveID consistent: Use the same liveID for all CoGuestState operations throughout the page lifecycle.CoGuestState only moves the user onto the seat; it does not automatically enable media devices.isAccept is true in onGuestApplicationResponded, or after calling acceptInvitation), manually invoke DeviceState methods to turn on your media devices.Was this page helpful?
You can also Contact sales or Submit a Ticket for help.
Help us improve! Rate your documentation experience in 5 mins.
Feedback