import { Call, StreamVideoClient } from "@stream-io/video-react-sdk";
let client: StreamVideoClient; // ...
const call: Call = client.call(type, id);
// load existing call information from our servers
await call.get();
// Creates the call on our servers in case it doesn't exist. Otherwise,
// loads the call information from our servers.
await call.getOrCreate();
// join the call
await call.join();
// leave the call and dispose all allocated resources
await call.leave();Calling State and Lifecycle
The call instance manages all call-related functionality: joining, actions (mute, reactions), and event subscriptions. Create instances via client.call(type, id). The StreamVideoClient maintains the WebSocket connection and handles API calls.
Best Practices
- Create call instances only in effects with
call.leave()cleanup to prevent memory leaks. - Handle all
CallingStatevalues exhaustively to show appropriate UI for each state. - Use
useCallCallingState()hook for reactive state updates in React components. - Reuse call instances or create new ones with
client.call(type, id)to rejoin.
Call instance lifecycle:
Access calling state via:
call.state.callingState- current valuecall.state.callingState$- observable for changesuseCallCallingState()- React hook
Call Instance
Acquire with client.call(), dispose with call.leave(). Always create in effects with cleanup:
const [call, setCall] = useState<Call | undefined>(undefined);
useEffect(() => {
const myCall = client.call(callType, callId);
myCall.join({ create: true }).then(
() => setCall(myCall),
() => console.error("Failed to join the call"),
);
return () => {
myCall.leave().catch(() => console.error("Failed to leave the call"));
setCall(undefined);
};
}, [callType, callId]);To rejoin, reuse the instance or create a new one with client.call(type, id).
Calling State
The CallingState enum exposes call state:
import { CallingState, useCallStateHooks } from "@stream-io/video-react-sdk";
const { useCallCallingState } = useCallStateHooks();
const callingState = useCallCallingState();
switch (callingState) {
case CallingState.JOINED:
// ...
break;
default:
const exhaustiveCheck: never = callingState;
throw new Error(`Unknown calling state: ${exhaustiveCheck}`);
}Handle CallingState exhaustively - TypeScript will warn if new states are added.
Calling States
| State | Description |
|---|---|
CallingState.UNKNOWN | The state is unknown. This value is set when Calling State isn't initialized properly. |
CallingState.IDLE | A call instance is created on the client side but a WebRTC session isn't established yet. |
CallingState.RINGING | This is an incoming (ring) call. You are the callee. |
CallingState.JOINING | The call join flow is executing (typically right after call.join()). Our systems are preparing to accept the new call participant. |
CallingState.JOINED | The join flow has finished successfully and the current participant is part of the call. The participant can receive and publish audio and video. |
CallingState.LEFT | The call has been left (call.leave()) and all allocated resources are released. Please create a new call instance if you want to re-join. |
CallingState.RECONNECTING | A network connection has been lost (due to various factors) and the call instance attempts to re-establish a connection and resume the call. |
CallingState.RECONNECTING_FAILED | The SDK failed to recover the connection after a couple of consecutive attempts. You need to inform the user that he needs to go online and manually attempt to rejoin the call. |
CallingState.MIGRATING | The SFU node that is hosting the current participant is shutting down or tries to rebalance the load. This call instance is being migrated to another SFU node. |
CallingState.OFFLINE | No network connection can be detected. Once the connection restores, the SDK will automatically attempt to recover the connection (signalled with RECONNECTING state). |
Calling State transitions: regular path
State transitions when joining a call as caller or callee:
stateDiagram-v2
direction LR
[*] --> IDLE
IDLE --> RINGING: call.join({ ring })
RINGING --> JOINING
IDLE --> JOINING: call.join()
JOINING --> JOINED
JOINED --> LEFT: call.leave()
LEFT --> [*]Calling State transitions: reconnects and migration
State transitions after network interruptions or during SFU node shutdown/rebalancing:
stateDiagram-v2
direction LR
[*] --> JOINING
JOINING --> JOINED: successful join
JOINED --> RECONNECTING: network glitch/switch
RECONNECTING --> JOINING: attempt to reconnect
RECONNECTING --> RECONNECTING_FAILED
JOINED --> LEFT: call.leave()
LEFT --> [*]
JOINED --> MIGRATING: initiate server migration
MIGRATING --> JOINING: attempt to migrate
JOINED --> OFFLINE: went offline
OFFLINE --> RECONNECTINGExample handling
import {
CallingState,
useCall,
useCallStateHooks,
} from "@stream-io/video-react-sdk";
const call = useCall();
const isCallCreatedByMe = call?.isCreatedByMe;
const { useCallCallingState } = useCallStateHooks();
const callingState = useCallCallingState();
switch (callingState) {
case CallingState.UNKNOWN:
case CallingState.IDLE:
return <LobbyScreen />;
case CallingState.RINGING:
return isCallCreatedByMe ? <OutgoingCallScreen /> : <IncomingCallScreen />;
case CallingState.JOINING:
return <LoadingCallScreen />;
case CallingState.JOINED:
return <ActiveCallScreen />;
case CallingState.LEFT:
return <HaveANiceDayScreen />;
case CallingState.RECONNECTING:
case CallingState.MIGRATING:
return <RestoringConnectionScreen />;
case CallingState.RECONNECTING_FAILED:
return <GeneralConnectionErrorScreen />;
case CallingState.OFFLINE:
return <NoConnectionScreen />;
default:
const exhaustiveCheck: never = callingState;
throw new Error(`Unknown calling state: ${exhaustiveCheck}`);
}