Client & Authentication

Set up StreamVideoClient to connect users and join calls.

Best Practices

  • Create StreamVideoClient once at app root; use tokenProvider for auto-refresh.
  • Always clean up with client.disconnectUser() in useEffect return.
  • Use short-lived tokens with a tokenProvider for production.
  • Handle connection errors with try/catch around connectUser().

Basic setup

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 });
  • 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:

PropertyValue
API Keymmhfdzb5evj2
TokeneyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3Byb250by5nZXRzdHJlYW0uaW8iLCJzdWIiOiJ1c2VyL1RocmlsbGluZ19DbG92ZXIiLCJ1c2VyX2lkIjoiVGhyaWxsaW5nX0Nsb3ZlciIsInZhbGlkaXR5X2luX3NlY29uZHMiOjYwNDgwMCwiaWF0IjoxNzcxNDA1MjcyLCJleHAiOjE3NzIwMTAwNzJ9.ydXnPNBq-VTCbRe6_JWW6Bbt5UUbh2c7mUVH5LGG8tw
User IDThrilling_Clover
Call IDEhbmenSskpoYMdExL2ozG

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).

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.

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

Authenticate users with a string token or a tokenProvider function that returns Promise<string>.

When using `tokenProvider`, the SDK automatically refreshes expired tokens.
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 });

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-native-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:

LevelSeverity
trace0
debug1
info2
warn3
error4
The SDK only logs errors that aren't re-thrown. API errors should be handled in your integration code.
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>
  );
};