Quickstart

This quickstart gives a concise overview of how Stream's React Video SDK works.

Best Practices

  • Create StreamVideoClient once at app root; clean up with disconnectUser() on unmount.
  • Use tokenProvider with short-lived tokens instead of static tokens.
  • Create and join calls in useEffect; clean up with call.leave().
  • Handle all callingState values in your UI (RINGING, JOINING, JOINED, etc.).
  • Handle errors from call.join(), device toggles, and connectUser().
  • Browser permissions: users get one chance to grant camera/mic access - handle denials gracefully.
  • Add a user interaction (button click) before joining to satisfy browser autoplay policies.

See Integration Best Practices for the complete checklist.

Installation

npm install @stream-io/video-react-sdk
# or
yarn add @stream-io/video-react-sdk

SDK Size

The SDK is optimized for tree-shaking. After minification it's ~600KB, or ~165KB gzipped. Check Bundlephobia for current stats.

Client Setup & Calls

Create a StreamVideoClient at the app root, then create and join calls as needed:

import { useEffect, useState } from "react";
import {
  Call,
  StreamCall,
  StreamVideo,
  StreamVideoClient,
} from "@stream-io/video-react-sdk";

const apiKey = "your-api-key";
const userId = "user-id";
const token = "authentication-token";
const user: User = { id: userId };

export const MyApp = () => {
  const [client, setClient] = useState<StreamVideoClient>();

  useEffect(() => {
    const myClient = new StreamVideoClient({ apiKey, user, token });
    setClient(myClient);

    return () => {
      myClient.disconnectUser().catch(console.error);
      setClient(undefined);
    };
  }, []);

  if (!client) return null;

  return (
    <StreamVideo client={client}>
      <MyCallUI client={client} />
    </StreamVideo>
  );
};

const MyCallUI = ({ client }: { client: StreamVideoClient }) => {
  const [call, setCall] = useState<Call>();

  useEffect(() => {
    const myCall = client.call("default", "my-first-call");
    myCall.join({ create: true }).catch(console.error);
    setCall(myCall);

    return () => {
      myCall.leave().catch(console.error);
      setCall(undefined);
    };
  }, [client]);

  if (!call) return null;

  return <StreamCall call={call}>{/* <MyVideoUI /> */}</StreamCall>;
};

default is a call type. There are 4 built-in call types and you can also create your own. The call type controls the permissions and which features are enabled.

The second argument is the call id. Call IDs can be reused, so you can join the same id multiple times (for example, for recurring meetings). To load an existing call without joining:

const call = client.call("default", "my-first-call");
await call.getOrCreate();

Rendering Video

Access call state with hooks from useCallStateHooks:

import { useCallStateHooks, ParticipantView } from "@stream-io/video-react-sdk";

export const MyVideoUI = () => {
  const { useParticipants } = useCallStateHooks();
  const participants = useParticipants();
  return (
    <>
      {participants.map((p) => (
        <ParticipantView participant={p} key={p.sessionId} />
      ))}
    </>
  );
};

The participant object contains audio/video tracks, user info, and enabled states. sessionId uniquely identifies each participant.

More: Call & Participant State

Camera & Microphone

import { useCallStateHooks } from "@stream-io/video-react-sdk";

export const MyVideoButton = () => {
  const { useCameraState } = useCallStateHooks();
  const { camera, isMute } = useCameraState();
  return (
    <button onClick={() => camera.toggle()}>
      {isMute ? "Turn on camera" : "Turn off camera"}
    </button>
  );
};

export const MyMicrophoneButton = () => {
  const { useMicrophoneState } = useCallStateHooks();
  const { microphone, isMute } = useMicrophoneState();
  return (
    <button onClick={() => microphone.toggle()}>
      {isMute ? "Turn on microphone" : "Turn off microphone"}
    </button>
  );
};

More: Camera & Microphone

UI Components

You can:

  • Build custom UI using state hooks
  • Use built-in SDK components
  • Mix both approaches

Built-in components support theming and props customization. See the UI Cookbook for custom component examples.

Using Built-in Components

import {
  CallControls,
  SpeakerLayout,
  StreamCall,
  StreamTheme,
  StreamVideo,
} from "@stream-io/video-react-sdk";

import "@stream-io/video-react-sdk/dist/css/styles.css";

export const MyApp = () => (
  <StreamVideo client={client}>
    <StreamCall call={call}>
      <StreamTheme>
        <SpeakerLayout participantBarPosition="right" />
        <CallControls />
      </StreamTheme>
    </StreamCall>
  </StreamVideo>
);

See It in Action

Try the CodeSandbox example to explore the SDK.