import { StreamVideoClient, User } from "@stream-io/video-react-native-sdk";
const user: User = { id: "sara" };
const apiKey = "my-stream-api-key";
const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
const client = StreamVideoClient.getOrCreateInstance({ apiKey, token, user });Client & Authentication
Client & Auth
Best Practices
- Use singleton pattern - Always use
getOrCreateInstance()to avoid multiple client instances - Use tokenProvider - Prefer
tokenProviderover static tokens for automatic refresh on expiry - Clean up on logout - Call
disconnectUser()when the user logs out - Server-side token generation - Never generate tokens client-side in production
Set up the video client before joining a call. Basic example:
- API Key - Found in your dashboard
- User types - Authenticated, anonymous, or guest
- Custom data - Store additional data on the user object as needed
Initialize the client when your application loads and use a context provider to make it available throughout your app.
Generating a token
Generate tokens server-side using our server SDKs. Integrate token generation into your login/registration flow. Tokens authenticate users and control call access.
Here are credentials to try out the app with:
| Property | Value |
|---|---|
| API Key | mmhfdzb5evj2 |
| Token | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3Byb250by5nZXRzdHJlYW0uaW8iLCJzdWIiOiJ1c2VyL1lpZWxkaW5nX1dhc3RlIiwidXNlcl9pZCI6IllpZWxkaW5nX1dhc3RlIiwidmFsaWRpdHlfaW5fc2Vjb25kcyI6NjA0ODAwLCJpYXQiOjE3NzAzOTUwOTIsImV4cCI6MTc3MDk5OTg5Mn0.nnKYTmZCR7BwAopJ6GZDoY38ovsaazdV8MoKLIUZwhQ |
| User ID | Yielding_Waste |
| Call ID | S6igOfKiOZMzn6S0bCtc6 |
For development purposes, you can use our Token Generator.
Different types of users
- Authenticated users - Users with an account on your app
- Guest users - Temporary accounts with a name and image for joining calls
- Anonymous users - Unauthenticated users, typically for watching livestreams
Guest users
Guest user setup:
import { StreamVideoClient, User } from "@stream-io/video-react-native-sdk";
const user: User = { id: "jack-guest", type: "guest" };
const apiKey = "my-stream-api-key";
const client = StreamVideoClient.getOrCreateInstance({ apiKey, user });Anonymous users
Anonymous user setup:
import { StreamVideoClient, User } from "@stream-io/video-react-native-sdk";
const user: User = { type: "anonymous" };
const apiKey = "my-stream-api-key";
const client = StreamVideoClient.getOrCreateInstance({ apiKey, user });Anonymous users don't establish a WebSocket connection and won't receive events. They can only watch livestreams or join calls.
The anonymous user token must include the call_cids field - an array of call CIDs the user can join.
Example JWT token payload for anonymous users:
{
"iss": "@stream-io/dashboard",
"iat": 1726406693,
"exp": 1726493093,
"user_id": "!anon",
"role": "viewer",
"call_cids": ["livestream:123"]
}Connecting a user and error handling
Connect users in two ways:
Automatic connection - when creating the client:
const client = new StreamVideoClient({
apiKey,
user,
token,
options: {
maxConnectUserRetries: 3,
onConnectUserError: (err: Error, allErrors: Error[]) => {
console.error("Failed to connect user", err, allErrors);
// handle the connect error, i.e. ask the user to retry
// later when they have better connection or show an error message
},
},
});Manual connection - using client.connectUser():
const client = new StreamVideoClient({ apiKey });
try {
await client.connectUser(user, token);
} catch (err) {
console.error("Failed to connect user", err);
// handle the connect error
}Disconnecting a user
Discard a client instance or disconnect a user with client.disconnectUser():
await client.disconnectUser();Client options
token or tokenProvider
Authenticate users with either a string token or a tokenProvider function returning Promise<string>.
When using a tokenProvider, the SDK will automatically execute it to refresh the token whenever the token expires.
import { StreamVideoClient, User } from "@stream-io/video-react-native-sdk";
const tokenProvider = async () => {
const response = await fetch("/api/token");
const data = await response.json();
return data.token;
};
const user: User = { id: "sara" };
const apiKey = "my-stream-api-key";
const client = StreamVideoClient.getOrCreateInstance({
apiKey,
tokenProvider,
user,
});Reject incoming call when busy
Configure the client to auto-reject incoming calls when the user is already in a call by setting rejectCallWhenBusy to true.
import { StreamVideoClient } from "@stream-io/video-react-native-sdk";
const client = StreamVideoClient.getOrCreateInstance({
apiKey,
tokenProvider,
user,
options: { rejectCallWhenBusy: true },
});The SDK plays a busy tone on the caller's side when the callee rejects due to being busy.
Logging
The SDK uses scoped logging. Each scope represents a module or group of modules.
Configure a sink and log level per scope. The SDK uses the default scope when no specific settings exist. A sink is a function that receives logs with level, message, and additional arguments.
Log levels by severity:
- trace: 0
- debug: 1
- info: 2
- warn: 3
- error: 4
Setting log level to warn for event-dispatcher scope shows only errors and warnings for that scope.
Note that our SDK only logs errors that do not get re-thrown. Using our API (producing HTTP request) might result in errors which should be handled within individual integrations.
import { StreamVideoClient } from "@stream-io/video-react-native-sdk";
const client = StreamVideoClient.getOrCreateInstance({
apiKey,
token,
user,
options: {
logOptions: {
default: {
level: "info",
},
coordinator: {
level: "warn",
sink: (logLevel, message, ...rest) => {
switch (logLevel) {
case "warn": {
console.warn(message, ...rest);
break;
}
case "error": {
SuperLogger.error(message, ...rest);
}
}
},
},
},
},
});Augment the default scope settings to avoid configuring each SDK scope individually.
import { videoLoggerSystem } from "@stream-io/video-client";
import SuperLogger from "./SuperLogger";
videoLoggerSystem.configureLoggers({
default: {
level: "info",
sink: (logLevel, message, ...rest) => {
SuperLogger[logLevel](message, ...rest);
},
},
});Reset specific scopes to defaults at runtime:
import { videoLoggerSystem } from "@stream-io/video-client";
videoLoggerSystem.configureLoggers({
"event-dispatcher": {
level: null,
sink: null,
},
});This resets only event-dispatcher; other scopes remain unchanged. To reset all defaults:
import { videoLoggerSystem } from "@stream-io/video-client";
videoLoggerSystem.restoreDefaults();StreamVideo context provider
Use the StreamVideo context provider to make the SDK client available throughout your application. This example uses tokenProvider for server-side auth:
import { useEffect, useState } from "react";
import {
StreamVideo,
StreamVideoClient,
User,
} from "@stream-io/video-react-native-sdk";
const apiKey = "my-stream-api-key";
const user: User = { id: "sara", name: "Sara" };
export const MyApp = () => {
const [client, setClient] = useState<StreamVideoClient>();
useEffect(() => {
const tokenProvider = () => Promise.resolve("<token>");
const myClient = StreamVideoClient.getOrCreateInstance({
apiKey,
user,
tokenProvider,
});
setClient(myClient);
return () => {
myClient.disconnectUser();
setClient(undefined);
};
}, []);
if (!client) return null;
return (
<StreamVideo client={client}>
<MyVideoApp />
</StreamVideo>
);
};