Phone & video calling
A complete audio/video calling system built on VideoSDK - ready with zero extra props.
Overview
Call buttons appear in the chat header for DMs and groups. The package ships a complete, ready-to-use calling system built on VideoSDK - it works the moment your backend's calling routes are live (the /calls/* REST and call_* socket contract). No VideoSDK API keys are ever in the frontend; your backend signs short-lived join tokens.
What “built-in” gets you
Pressing the phone/video button calls the package's own useCallStore().startCall(…) - no features.onPhoneCall needed. You get:
- Ringing UI on both sides (
IncomingCallDialog), busy detection, and missed / declined / cancelled call messages in the chat timeline. - A full in-call surface (
CallOverlay): mic, camera, leave, screen share, pin a tile, grid/speaker layout for groups, emoji reactions, raise hand, in-call chat and a participants panel. - One presenter at a time. While someone shares, others see Ask to share your screen; the presenter gets a toast with Stop sharing. The host gets Take over screen share instead.
- Cloud recording - host-only start/stop, a live “● REC” badge for everyone and a download link once processed. Gated by
callRecordingEnabled. It follows the call's live mode, so an audio call switched to video records as video. - Transcripts - when the backend produces one, the recording gets a transcript (status in the Recordings drawer and preview). Gated by
callTranscriptionEnabled; there's no separate start button. - Join an ongoing call - a banner with Join in the chat header while a group call runs, and live calls in the Calls drawer (
GET /calls/active,POST /calls/:callId/join). - Calls & Recordings drawers - history and recordings per chat, from the header options menu (
CallsDrawer/RecordingsDrawerslots). - Collaborative whiteboard - a shared drawing board; “Share PDF to chat” uploads its export as an attachment. Gated by
whiteboardEnabled. - The call pops into its own browser window when possible (falls back to the panel if the popup is blocked), so it keeps running while the user browses elsewhere.
Restyle, hide or replace any in-call control with features.callControls - see In-call control bar.
Requirement: @videosdk.live/react-sdk
Calling needs @videosdk.live/react-sdk pinned to ^0.13.3. It's already bundled, so npm/yarn hosts need nothing extra. pnpm + Vite hosts list "@realtimexsco/live-chat > @videosdk.live/react-sdk" in optimizeDeps.include (see Project setup).
Option A - Use your own call SDK
Providing onPhoneCall / onVideoCall fully replacesthe built-in VideoSDK flow - it doesn't run alongside it. Omit both to keep the built-in system.
features={{ showPhoneCall: true, showVideoCall: true, onPhoneCall: ({ activeChannel, conversationId }) => { yourVoiceSDK.start(activeChannel.id, conversationId); }, onVideoCall: ({ activeChannel, conversationId }) => { yourVideoSDK.start(activeChannel.id, conversationId); },}}Option B - Replace the call button group
components={{ HeaderCallActions: ({ onPhoneCall, onVideoCall, activeChannel }) => ( <div className="flex gap-2"> <button type="button" onClick={onPhoneCall}>Call {activeChannel.name}</button> <button type="button" onClick={onVideoCall}>Video</button> </div> ),}}Option C - Replace individual buttons
components={{ PhoneCallButton: ({ onPhoneCall, className }) => ( <button type="button" className={className} onClick={onPhoneCall}>📞</button> ), VideoCallButton: ({ onVideoCall, className }) => ( <button type="button" className={className} onClick={onVideoCall}>📹</button> ),}}Custom buttons should respect the admin's settings - useAdminFeatureGate("videoCall").
Hide calls
features={{ showPhoneCall: false, showVideoCall: false }}To hide the in-call UI or ringing dialog, pass CallOverlay: () => null or IncomingCallDialog: () => null in components.
Calling exports
import { useCallStore, CALL_STATUS } from "@realtimexsco/live-chat"; const uiStatus = useCallStore((s) => s.uiStatus);const isRecording = useCallStore((s) => s.recordingStatus === "recording");