import { StreamVideoClient, User } from "@stream-io/video-react-sdk";
const user: User = { id: "sara" };
const apiKey = "my-stream-api-key"; // from dashboard.getstream.io
const tokenProvider = async () => fetchTokenFromYourServer(user.id);
const client = new StreamVideoClient({ apiKey, user, tokenProvider });Client & Authentication
Set up StreamVideoClient to connect users and join calls.
Best Practices
- Create
StreamVideoClientonce at app root; usetokenProviderfor auto-refresh. - Always clean up with
client.disconnectUser()in useEffect return. - Use short-lived tokens with a
tokenProviderfor production. - Handle connection errors with try/catch around
connectUser().
Basic setup
- The API Key can be found in your dashboard.
- The user can be either authenticated, anonymous or guest.
- You can store custom data on the user object if required.
Typically, initialize the client when your application loads and use a context provider to make it available throughout your app.
Generating a token
Tokens must be generated server-side. Use our server-side SDKs to add support for this - typically integrated with your login/registration flow.
Here are credentials to try out the app with:
| Property | Value |
|---|---|
| API Key | mmhfdzb5evj2 |
| Token | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3Byb250by5nZXRzdHJlYW0uaW8iLCJzdWIiOiJ1c2VyL1F1aXJreV9Sb3F1ZWZvcnQiLCJ1c2VyX2lkIjoiUXVpcmt5X1JvcXVlZm9ydCIsInZhbGlkaXR5X2luX3NlY29uZHMiOjYwNDgwMCwiaWF0IjoxNzczMTQwMTE2LCJleHAiOjE3NzM3NDQ5MTZ9.eoiJRzOxdAg5lKe8wiCcpD3c_P3CERsdB9xGYpIo898 |
| User ID | Quirky_Roquefort |
| Call ID | X55kiDoO8OYIqLhuxu8h6 |
For development purposes, you can use our Token Generator.
User types
- Authenticated users - users with an account in your app.
- Guest users - temporary accounts with a name and image.
- Anonymous users - unauthenticated users (common for livestream viewers).
For authenticated users (default user.type), pass either token or
tokenProvider. For guest users, do not pass token or tokenProvider.
For anonymous users, token and tokenProvider are optional.
Guest users
Example setup for a guest user:
import { StreamVideoClient, User } from "@stream-io/video-react-sdk";
const user: User = { id: "jack-guest", type: "guest" };
const apiKey = "my-stream-api-key";
const client = new StreamVideoClient({ apiKey, user });Anonymous users
Example for an anonymous user:
import { StreamVideoClient, User } from "@stream-io/video-react-sdk";
const user: User = { type: "anonymous" };
const apiKey = "my-stream-api-key";
const client = new StreamVideoClient({ apiKey, user });Anonymous users don't establish a WebSocket connection, so they won't receive events. They can only watch livestreams or join calls.
You can also provide a call-scoped token (or token provider) for anonymous users:
import { StreamVideoClient, User } from "@stream-io/video-react-sdk";
const user: User = { type: "anonymous" };
const apiKey = "my-stream-api-key";
const token = "<anonymous-token-with-call_cids>";
const client = new StreamVideoClient({ apiKey, user, token });The token for anonymous users should include call_cids - an array of call CIDs they're allowed to join:
{
"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
For authenticated users, pass a string token or a tokenProvider function
that returns Promise<string>. You can also provide both together. Anonymous
users can also pass token or tokenProvider when needed.
import { StreamVideoClient, User } from "@stream-io/video-react-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 = new StreamVideoClient({ apiKey, tokenProvider, user });Or initialize with a static token:
import { StreamVideoClient, User } from "@stream-io/video-react-sdk";
const user: User = { id: "sara" };
const apiKey = "my-stream-api-key";
const token = "<token-from-your-server>";
const client = new StreamVideoClient({ apiKey, user, token });When both are provided, the SDK uses token first and then tokenProvider for subsequent token refreshes:
import { StreamVideoClient, User } from "@stream-io/video-react-sdk";
const user: User = { id: "sara" };
const apiKey = "my-stream-api-key";
const token = "<token-from-your-server>";
const tokenProvider = async () => fetchTokenFromYourServer(user.id);
const client = new StreamVideoClient({ apiKey, user, token, tokenProvider });Device persistence
Device preferences are persisted by default on web. Configure or disable this with options.devicePersistence when creating the client. See the Camera & Microphone guide.
Reject incoming call when busy
Auto-reject incoming ringing calls when the user is already in a call:
import { StreamVideoClient } from "@stream-io/video-react-sdk";
const client = new StreamVideoClient({
apiKey,
tokenProvider,
user,
options: { rejectCallWhenBusy: true },
});Logging
The SDK uses scoped logging with configurable levels per module. Log levels ordered by severity:
| Level | Severity |
|---|---|
| trace | 0 |
| debug | 1 |
| info | 2 |
| warn | 3 |
| error | 4 |
import { StreamVideoClient, Logger } from "@stream-io/video-react-sdk";
const client = new StreamVideoClient({
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);
}
}
},
},
},
},
});Configure the default scope to apply settings globally:
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:
import { videoLoggerSystem } from "@stream-io/video-client";
videoLoggerSystem.configureLoggers({
"event-dispatcher": {
level: null,
sink: null,
},
});Reset all scopes to defaults:
import { videoLoggerSystem } from "@stream-io/video-client";
videoLoggerSystem.restoreDefaults();Sentry integration
Example Sentry integration:
import { LogLevel, Sink, logToConsole } from "@stream-io/video-client";
import * as Sentry from "@sentry/nextjs";
const logLevelMapping = new Map<LogLevel, Sentry.SeverityLevel>();
logLevelMapping.set("debug", "debug");
logLevelMapping.set("info", "info");
logLevelMapping.set("warn", "warning");
logLevelMapping.set("error", "error");
export const customSentryLogger: Sink = (
logLevel: LogLevel,
message: string,
...args: unknown[]
) => {
if (logLevel === "warn" || logLevel === "error") {
Sentry.captureEvent({
level: logLevelMapping.get(logLevel),
extra: args,
});
}
// Call the SDK's default log method
logToConsole(logLevel, message, { data: "some data" });
};StreamVideo context provider
The StreamVideo provider makes the client available throughout your app:
import { useEffect, useState } from "react";
import {
StreamVideo,
StreamVideoClient,
User,
} from "@stream-io/video-react-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 = new StreamVideoClient({ apiKey, user, tokenProvider });
setClient(myClient);
return () => {
myClient.disconnectUser();
setClient(undefined);
};
}, []);
if (!client) return null;
return (
<StreamVideo client={client}>
<MyVideoApp />
</StreamVideo>
);
};