Prebuilt Components

Prebuilt components are drop-in React components that handle the entire call lifecycle in a single component. They manage client initialization, call joining, the complete meeting or livestream UI, and cleanup when the component is removed. You don't need to set up StreamVideoClient, wire up providers, or build any UI yourself.

Use EmbeddedCall for video and audio meetings, or EmbeddedLivestream for live streaming. Both components only require an API key, call type, call ID, and user identity to render a fully functional experience.

For full control over the UI and call behavior, see the Quickstart for manual integration.

Getting Started

Installation

npm install @stream-io/video-react-sdk

Import components from /embedded and the dedicated stylesheet:

import {
  EmbeddedCall,
  EmbeddedLivestream,
} from "@stream-io/video-react-sdk/embedded";
import "@stream-io/video-react-sdk/dist/css/embedded.css";

Use embedded.css instead of styles.css when using prebuilt components.

Call Creation

The call must exist before the prebuilt component can join it. Because the component manages the client internally, you need to create the call on your server using a server-side SDK. The callType and callId props must match the call you created on the server.

See also:

EmbeddedCall

For video and audio meetings. The callType should match a call type configured on your dashboard (e.g., "default").

Lifecycle

  1. Lobby: Camera/mic preview, device settings, display name.
  2. Joining: Connects and sets up media streams.
  3. Active Call: Full meeting UI with all controls.
  4. Feedback: Star rating (1-5) and optional text feedback.

Features

  • Camera and microphone controls
  • Screen sharing
  • Reactions
  • Recording controls
  • Noise cancellation
  • Background blur
  • Participant sidebar
  • Connection status notifications
  • Call duration display
  • Wake lock
import { EmbeddedCall } from "@stream-io/video-react-sdk/embedded";
import "@stream-io/video-react-sdk/dist/css/embedded.css";

export const MyMeeting = () => (
  <EmbeddedCall
    apiKey="YOUR_API_KEY"
    callType="default"
    callId="my-meeting"
    user={{ type: "authenticated", id: "user-1", name: "John" }}
    token="user-token"
  />
);

EmbeddedLivestream

For livestreaming. The component automatically shows either the host or viewer UI based on the user's permissions. Users with the JOIN_BACKSTAGE capability see the host UI; everyone else sees the viewer UI. You can configure this through call type permissions on the dashboard.

Lifecycle

StageHostViewer
1. Pre-joinLobby: camera/mic preview, device settingsWaiting lobby: countdown timer, auto-join option
2. ConnectedBackstage: prepare before going live(waits for stream to start)
3. LiveBroadcasting: go-live/stop controls, viewer count, elapsed timeWatching: live stream with viewer count
4. EndStops the streamLeaves or stream ends
5. FeedbackStar rating and text feedbackStar rating and text feedback

Host Features

  • Go-live / stop-live controls
  • Viewer count and elapsed stream time
  • Backstage mode
  • Camera, microphone, and screen sharing controls

Viewer Features

  • Countdown timer and auto-join on go-live
  • Viewer count
import { EmbeddedLivestream } from "@stream-io/video-react-sdk/embedded";
import "@stream-io/video-react-sdk/dist/css/embedded.css";

// Host: requires an authenticated user with JOIN_BACKSTAGE capability
export const HostView = () => (
  <EmbeddedLivestream
    apiKey="YOUR_API_KEY"
    callType="livestream"
    callId="my-stream"
    user={{ type: "authenticated", id: "host-1", name: "Jane" }}
    token="host-token"
  />
);

// Viewer: anonymous users see the viewer UI
export const ViewerView = () => (
  <EmbeddedLivestream
    apiKey="YOUR_API_KEY"
    callType="livestream"
    callId="my-stream"
    user={{ type: "anonymous" }}
  />
);

Props

Both components accept the same props:

NameTypeDescriptionDefault
apiKeystringStream API key. Required.
callTypestringCall type (e.g., "default", "livestream"). Required.
callIdstringUnique call identifier. Required.
userEmbeddedUserUser identity. See User Authentication. Required.
tokenstringStatic auth token. Required for authenticated users when tokenProvider is not set.undefined
tokenProvider() => Promise<string>Returns a fresh token. Recommended for production.undefined
logLevelLogLevelLogging level: "trace", "debug", "info", "warn", "error".undefined
layoutLayoutOptionParticipant layout. See Layout Options."SpeakerTop"
themeCSSPropertiesCSS variable overrides for theming. See Styling.undefined
onError(error: any) => voidError callback.undefined
childrenReactNodeContent rendered alongside the default UI.undefined

User Authentication

The user prop supports three authentication modes:

Authenticated

A registered user in your app. Requires either a token or tokenProvider.

<EmbeddedCall
  apiKey="YOUR_API_KEY"
  callType="default"
  callId="my-meeting"
  user={{ type: "authenticated", id: "user-1", name: "John" }}
  token="user-token"
/>

In production, use tokenProvider instead so tokens are refreshed automatically:

<EmbeddedCall
  apiKey="YOUR_API_KEY"
  callType="default"
  callId="my-meeting"
  user={{ type: "authenticated", id: "user-1", name: "John" }}
  tokenProvider={async () => {
    const res = await fetch("/api/stream-token");
    const data = await res.json();
    return data.token;
  }}
/>

Guest

A temporary user with server-generated credentials. No token needed.

<EmbeddedCall
  apiKey="YOUR_API_KEY"
  callType="default"
  callId="my-meeting"
  user={{ type: "guest", id: "guest-1", name: "Visitor" }}
/>

Anonymous

Joins without any identity. Useful for open calls or public livestreams.

<EmbeddedCall
  apiKey="YOUR_API_KEY"
  callType="default"
  callId="my-meeting"
  user={{ type: "anonymous" }}
/>

Layout Options

LayoutDescription
"PaginatedGrid"Equal-sized tiles in a grid, paginated.
"SpeakerLeft"Active speaker left, others right.
"SpeakerRight"Active speaker right, others left.
"SpeakerTop"Active speaker top, others below. (Default)
"SpeakerBottom"Active speaker bottom, others above.

The layout automatically switches to a Speaker view when screen sharing starts.

<EmbeddedCall
  apiKey="YOUR_API_KEY"
  callType="default"
  callId="my-meeting"
  user={{ type: "authenticated", id: "user-1", name: "John" }}
  token="user-token"
  layout="PaginatedGrid"
/>

Error Handling

<EmbeddedCall
  apiKey="YOUR_API_KEY"
  callType="default"
  callId="my-meeting"
  user={{ type: "authenticated", id: "user-1", name: "John" }}
  token="user-token"
  onError={(error) => {
    console.error("Call error:", error);
  }}
/>

onError is called when the client fails to initialize, the user cannot join the call, or a media device error occurs.

Styling

The component expands to fill its parent container. To control the size, set dimensions on the wrapper element:

// Full screen
<div style={{ width: "100vw", height: "100vh" }}>
  <EmbeddedCall {/* ...props */} />
</div>

Via the theme prop

Pass CSS variables directly through the theme prop for per-instance theming:

<EmbeddedCall
  apiKey="YOUR_API_KEY"
  callType="default"
  callId="my-meeting"
  user={{ type: "authenticated", id: "user-1", name: "John" }}
  token="user-token"
  theme={{
    "--str-video__primary-color": "#005fff",
    "--str-video__background-color0": "#ffffff",
    "--str-video__background-color1": "#f7f7f8",
    "--str-video__text-color1": "#1c1e22",
    "--str-video__text-color2": "#72767e",
    "--str-video__button-default-base": "#eff0f1",
    "--str-video__button-default-hover": "#e3e4e5",
    "--str-video__icon-default": "#4c535b",
  }}
/>

Via CSS

Override CSS variables on .str-video in a stylesheet:

.str-video {
  --str-video__primary-color: #6002ee;
  --str-video__secondary-color: #90ee02;
  --str-video__text-color1: #282b2b;
  --str-video__border-radius-circle: 15px;
}

Import SDK styles before your own for easier overrides:

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

Use CSS cascade layers for finer control:

@import url("@stream-io/video-react-sdk/dist/css/embedded.css")
layer(video-default);

.str-video__call-controls__button {
  padding: 20px;
}

All available variables are listed in the theme variables file. See the Theme page for full theming documentation.

Custom Children

Children are rendered alongside the default UI, not as a replacement:

<EmbeddedCall
  apiKey="YOUR_API_KEY"
  callType="default"
  callId="my-meeting"
  user={{ type: "authenticated", id: "user-1", name: "John" }}
  token="user-token"
>
  <MyCustomSidePanel />
</EmbeddedCall>

Children have access to all SDK hooks since they render inside the StreamVideo and StreamCall providers. You can use this to respond to call state changes:

import { useEffect } from "react";
import { CallingState, useCallStateHooks } from "@stream-io/video-react-sdk";
import { EmbeddedCall } from "@stream-io/video-react-sdk/embedded";
import "@stream-io/video-react-sdk/dist/css/embedded.css";

const CallStateListener = () => {
  const { useCallCallingState } = useCallStateHooks();
  const callingState = useCallCallingState();

  useEffect(() => {
    if (callingState === CallingState.LEFT) {
      console.log("User has left the call");
    }
  }, [callingState]);

  return null;
};

export const MyApp = () => (
  <EmbeddedCall
    apiKey="YOUR_API_KEY"
    callType="default"
    callId="my-meeting"
    user={{ type: "authenticated", id: "user-1", name: "John" }}
    token="user-token"
  >
    <CallStateListener />
  </EmbeddedCall>
);

Audio Filters

To enable noise cancellation, install the @stream-io/audio-filters-web package:

npm install @stream-io/audio-filters-web

The prebuilt components will automatically detect the package and enable or disable noise cancellation based on your dashboard configuration. See the Noise Cancellation guide for more details.