const [client, setClient] = useState<StreamVideoClient>();
useEffect(() => {
const tokenProvider = async () => api.fetchToken(user.id);
const client = new StreamVideoClient({ apiKey, user, tokenProvider });
setClient(client);
return () => {
// dispose the client once you don't need it anymore
client.disconnectUser().catch((err) => console.error(err));
setClient(undefined);
};
}, [apiKey, user, tokenProvider]);
Integration Best Practices
After the integration is complete, and you prepare to deploy your application to your end users, please take the time to review the following list of best practices. This page contains a list of items you should consider taking into account when integrating Stream Video SDK to ensure a smooth experience for your users.
Client and Call initialization
client
and call
instances are stateful objects that have to be managed in your application.
Failing to do so can cause memory leaks, performance issues, or unexpected behavior (e.g., a call isn’t disposed, an the current user still publishes their video stream).
The general rule of thumb here is to create client
and call
inside a useEffect
hook and make sure they are disposed when they are no longer needed or when the component is unmounted.
StreamVideoClient
Ensure that you create only one instance of StreamVideoClient
in your application.
Running multiple instances of StreamVideoClient
can cause a processing overhead and reduce performance of the video calling experience and your application, too.
StreamVideoClient.getOrCreateInstance(...)
can help you to create a single instance of StreamVideoClient
.
Token and Token Providers
It is a good practice to always use a tokenProvider
with relatively short living tokens (~4 hours).
More: Client & Authentication.
Call
call
can be created only after the StreamVideoClient
is initialized.
import { useStreamVideoClient } from "@stream-video/react-sdk";
const client = useStreamVideoClient();
const [call, setCall] = useState<Call>();
useEffect(() => {
if (!client) return;
const call = client.call(type, id);
setCall(call);
// join the call
call.join().catch((err) => console.error(err));
return () => {
// dispose the call once you don't need it anymore
call.leave().catch((err) => console.error(err));
setCall(undefined);
};
}, [client, type, id]);
Every client.call(type, id)
call creates a new client-side call
instance.
In your UI components, it is recommended to use the useCall()
hook to access the call
instance, exposed by the <StreamCall call={call} />
context provider.
export const MyComponent = () => {
const call = useCall();
useEffect(() => {
if (!call) return;
// do something with the call
call.getOrCreate().catch((err) => console.error(err));
}, [call]);
};
call
is a client-side instance that doesn’t automatically connect to the server.
A call
is considered as initialized, and it’s server-side setting loaded after one of the following events operations are performed:
await call.get()
await call.create()
await call.getOrCreate()
await call.join()
Every call
instance provided by the useCalls()
hook is initialized.
Please ensure proper disposal of the call
instance by performing call.leave()
once you don’t need it anymore
as otherwise, the dangling call
instance will cause memory leaks, performance issues, and unexpected behavior.
More: Joining & Creating Calls.
Calling State
The call
instance has multiple states that can be used to determine the current state of the call.
Make sure you handle all possible states of the call and show an appropriate UI to the user.
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const { useCallingState } = useCallStateHooks();
const callingState = useCallingState();
More: Calling State and Lifecycle.
Device management
Permission prompt
Device management is a complex topic, especially when it comes to user permissions. The browser and the operating system may require user permissions to access the camera and microphone, usually at the time of the first call.
The user has only one chance to grant permissions to the application. If the user denies the permissions, the application will not be able to prompt the user for permissions again.
In this case, the SDK will not be able to access the camera and microphone, and without a proper UX, the user won’t know what the problem is and usually would complain that the call doesn’t work as expected.
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const { useCameraState, useMicrophoneState } = useCallStateHooks();
const { hasBrowserPermission, isPromptingPermission } = useCameraState();
console.log(
hasBrowserPermission
? "User has granted camera permissions!"
: "User has denied or not yet granted camera permissions!",
);
if (isPromptingPermission) {
// Useful to show certain UI while the prompt is pending
console.log("Waiting for user to respond to the browser permission prompt");
}
More: Camera & Microphone permissions.
Device listing and switching
Remember to implement a device switcher. Many users have multiple devices, such as a laptop with a built-in camera and microphone, and an external webcam or microphone. Their system default device might not be configured, and they may want to use a different device.
The SDK provides a few UI components to help you with this task: Device Settings.
More: List and select devices.
Persist device selection
Remember to persist the device selection. Users are not always happy to set up their devices every time they join a call. The SDK provides a utility hook to help you with this topic: usePersistedDevicePreferences.
import { usePersistedDevicePreferences } from "@stream-video/react-sdk";
export const MyComponent = () => {
usePersistedDevicePreferences();
return <>{children}</>;
};
Lobby
It is a good practice to implement a lobby screen, where users can check their devices, set the frame, and get ready to join the call.
More: Lobby UI cookbook.
Speaking while muted detection
Our SDK has a built-in feature to detect when the user is speaking while the microphone is muted. Although no audio is sent, this feature keeps the microphone on, and some users might not be comfortable with this behavior.
// to disable it:
await call.microphone.disableSpeakingWhileMutedNotification();
Audio and Video filters
Please be aware that the Noise Cancellation (Audio) and Background blurring / replacement (Video) filters put an additional pressure on the CPU and can cause a significant performance drop, especially on low-end devices.
Noise Cancellation will be automatically disabled if we detect CPU pressure. We don’t automatically disable Background blurring / replacement to protect the user’s privacy. However, it is recommended to advise your users to disable them in case they experience performance issues.
More: Audio & Video Filters.
Error handling
The public API of the SDK is asynchronous, and it’s possible that an error will occur during the execution of any SDK method.
It is a good practice to handle the possible Promise
rejections in your application.
Both approaches are valid try/await/catch
blocks or .then().catch()
chains.
Among the many possible errors and exceptions, please make sure you handle the following ones:
Device errors
try {
await call.microphone.enable();
await call.camera.enable();
} catch (err) {
// handle the error (log, show a toast, etc.)
console.error("Failed to enable a device", err);
}
Join errors
try {
await call.join();
} catch (err) {
// handle the error (log, show a toast, etc.)
console.error("Failed to join the call", err);
}
More: Join retries.
Connect user issues
const client = new StreamVideoClient({ apiKey });
try {
await client.connectUser(user, token);
} catch (err) {
console.error("Failed to connect user", err);
// handle the connect error
}
More: Connect user error handling.
Network
Firewall and proxy set up
Some of your customers might be behind a firewall or a proxy that limits the access to the internet. In this case, the SDK might not be able to connect to the server. In this case, the SDK will throw an error, and it is up to you to handle it.
In cases where you have control over the firewall or proxy, please make sure to apply the following settings: Networking and Firewall. Otherwise, please inform your customers to switch to a less restrictive network if possible.
We also have a Connection Test guide and a tool that can help you to test your network connection.
Reconnections
In bad network conditions, or when switching between different networks (e.g., Wi-Fi and mobile data), the SDK might lose the connection to the server.
In this case, the SDK will try to reconnect automatically. The callingState
of the reconnecting participant will reflect the state accordingly
but the remote participants will not be notified. In some cases, the local participant might temporarily “leave” the call.
In most of the cases, upon the temporary “leave” of the local participant, the SDK will automatically reconnect to the server and the call will resume as expected. This is important to keep in mind when implementing your UI, especially when it comes to the UI of the remote participants or ending a call. A common mistake is to immediately end the call or perform certain actions upon the “leave” event. Our recommendation is to wait for at least 60 seconds before performing any actions. This should give the SDK enough time to get a stable connection and reconnect to the server and resume the call.
More: Network Disruptions.
Low bandwidth
Unless opted-out, the SDK will automatically detect low bandwidth and automatically switch to a lower quality. In cases when the bandwidth is extremely low, the SDK will unsubscribe from the remote video streams to save bandwidth and allow more room for the incoming audio.
Once this happens, a good practice is to show an info message or an indicator to the user so they know what’s happening. The built-in SDK component and layouts have full support for this feature, but if you are using a custom layout, we highly recommend you implement the notification aspect of this feature yourself.
More: Low bandwidth.
User permissions
Please take the time to go through the list of available permissions for roles that you support on our dashboard. Hiding certain UI elements in the app isn’t enough as a malicious actor could still access the Stream API directly and bypass the application permissions checks.
More: Permissions & Moderation.
Gather feedback
Collecting user feedback is a good practice to improve the quality of your application and integration. It is also a good way to understand how your users are using the application and what features they would like to see in the future.
More: User Rating & Feedback.
Supported browsers
The Stream Video SDK supports the latest versions of the major browsers: Chrome, Firefox, Safari, Edge, including mobile browsers and webviews. Although the SDK works in other exotic browsers, we strongly recommend you advise your users to use one of the supported browsers for optimal experience.
We expose a utility API to help you with browser detection: Supported Platforms.
Keep dependencies up-to-date
We constantly improve our SDK and we regularly release new versions with new features and bug fixes. It is a good practice to keep your dependencies up to date to ensure that you are using the latest and greatest version of our SDK.
We provide detailed change logs for each release in our GitHub Releases section.