diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/02-tutorials/01-video-calling.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/02-tutorials/01-video-calling.mdx
index f8d968459f..ec565be47e 100644
--- a/packages/react-native-sdk/docusaurus/docs/reactnative/02-tutorials/01-video-calling.mdx
+++ b/packages/react-native-sdk/docusaurus/docs/reactnative/02-tutorials/01-video-calling.mdx
@@ -3,7 +3,12 @@ title: Video Call Tutorial
description: How to build a video call application similar to Zoom or Google Meet
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
import { TokenSnippet } from '../../../shared/_tokenSnippet.jsx';
+import homeScreenImg from '../assets/02-tutorials/01-video-calling/home-screen.png'
+import videoScreenEmpty from '../assets/02-tutorials/01-video-calling/video-empty.png'
This tutorial teaches you how to build Zoom/Whatsapp style video calling for your app.
@@ -12,7 +17,7 @@ This tutorial teaches you how to build Zoom/Whatsapp style video calling for you
- Video quality and codecs are automatically optimized.
- Powered by Stream's [Video Calling API](https://getstream.io/video/).
-### Step 1 - Setup a new React Native app
+## Step 1 - Setup a new React Native app
Create a new React Native app using the official template,
@@ -21,26 +26,31 @@ npx react-native@latest init VideoCallExample
cd VideoCallExample
```
-### Step 2 - Install the SDK and declare permissions
+## Step 2 - Install the SDK and declare permissions
-To install the Stream Video React Native SDK, run the following command in your terminal of choice:
+In order to install the Stream Video React Native SDK, run the following command in your terminal of choice:
```bash title=Terminal
-yarn add @stream-io/video-react-native-sdk
+yarn add @stream-io/video-react-native-sdk @stream-io/react-native-webrtc
```
The SDK requires installing some peer dependencies. You can run the following command to install them:
```bash title=Terminal
-yarn add @stream-io/react-native-webrtc react-native-device-info \
- react-native-incall-manager react-native-svg \
- @react-native-community/netinfo @notifee/react-native
+yarn add react-native-device-info@10.6.0
+yarn add react-native-incall-manager@4.1.0
+yarn add react-native-svg
+yarn add @react-native-community/netinfo@9.3.9
+yarn add @notifee/react-native@7.7.1
+
+# Install pods for iOS
npx pod-install
```
#### Add Stream Video SDK's setup method
-##### Android
+
+
Add the following in your `MainApplication.java` file:
@@ -60,9 +70,9 @@ public class MainApplication extends Application implements ReactApplication {
}
}
```
-
-##### iOS
+
+
Add the following in your `AppDelegate.m` or `AppDelegate.mm` file:
@@ -81,31 +91,37 @@ Add the following in your `AppDelegate.m` or `AppDelegate.mm` file:
}
```
-#### Declare permissions
-
-##### iOS
+
+
-Add the following keys and values to `Info.plist` file:
-
-- `Privacy - Camera Usage Description` - "`VideoCallExample` requires camera access to capture and transmit video"
-- `Privacy - Microphone Usage Description` - "`VideoCallExample` requires microphone access to capture and transmit audio"
+#### Declare permissions
-##### Android
+
+
-In `AndroidManifest.xml` add the following permissions before the `` section.
+In `AndroidManifest.xml` add the following permissions before the `application` section.
```xml
-
-
-
-
-
-
-
-
-
-
-
+
+ // highlight-start
+
+
+
+
+
+
+
+
+
+
+ // highlight-end
+
+ ...
+
+
+
```
If you plan to also support Bluetooth devices then also add the following.
@@ -116,8 +132,34 @@ If you plan to also support Bluetooth devices then also add the following.
```
+
+
+
+Add the following keys and values to `Info.plist` file, under `dict` tag.
+
+```plist title="Info.plist"
+
+
+ ...
+ CFBundleName
+ $(PRODUCT_NAME)
+ // highlight-start
+ NSCameraUsageDescription
+ $(PRODUCT_NAME) would like to use your camera
+ NSMicrophoneUsageDescription
+ $(PRODUCT_NAME) would like to use your microphone
+ // highlight-end
+ ...
+
+
+
+```
+
+
+
+
:::note
-For simplicity, in this tutorial, we do not cover about managing native runtime permissions. Before starting the next step, ensure that camera and microphone permissions are given for this app by granting them in the native settings app. We have discussed a detailed solution to manage native runtime permissions in the [Manage Native Permissions](../../core/native-permissions) guide.
+For simplicity, in this tutorial, we do not cover about managing native runtime permissions. Before starting the next step, ensure that microphone permissions are given for this app by granting them in the native settings app. We have discussed a detailed solution to manage native runtime permissions in the [Manage Native Permissions](../../core/native-permissions) guide.
:::
#### Android Specific installation
@@ -125,9 +167,14 @@ For simplicity, in this tutorial, we do not cover about managing native runtime
In `android/app/build.gradle` add the following inside the `android` section:
```java
-compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_11
+android {
+ ...
+ // highlight-start
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_11
+ }
+ // highlight-end
}
```
@@ -137,160 +184,413 @@ In `android/gradle.properties` add the following:
android.enableDexingArtifactTransform.desugaring=false
```
-### Step 3 - Create & Join a call
+#### Run the app
-Open up `src/App.tsx` and replace it with this code:
+To ensure the best possible experience, we highly recommend running the app on a physical device.
+This is due to the limitations in audio and video device support on emulators.
+You can refer to the React Native documentation for guidance on [running the app on a physical device](https://reactnative.dev/docs/running-on-device).
-```tsx title="App.tsx"
-import {
- StreamVideoClient,
- CallingState,
- StreamVideo,
- StreamCall,
- User,
- useCall,
- useCallStateHooks,
- StreamVideoRN,
-} from '@stream-io/video-react-native-sdk';
-import React from 'react';
-import { SafeAreaView, Text } from 'react-native';
+However, if you still prefer to use an emulator, execute the following command:
-// for simplicity, we assume that permission was granted through the native settings app
-StreamVideoRN.setPermissions({
- isMicPermissionGranted: true,
- isCameraPermissionGranted: true,
-});
+```bash
+# run iOS app
+yarn ios
-const apiKey = 'REPLACE_WITH_API_KEY'; // the API key can be found in the "Credentials" section
-const token = 'REPLACE_WITH_TOKEN'; // the token can be found in the "Credentials" section
-const userId = 'REPLACE_WITH_USER_ID'; // the user id can be found in the "Credentials" section
-const callId = 'REPLACE_WITH_CALL_ID'; // the call id can be found in the "Credentials" section
+# run Android app
+yarn android
+```
-// set up the user object
-const user: User = {
- id: userId,
- name: 'Santhosh',
- image: `https://getstream.io/random_png/?id=${userId}&name=Santhosh`,
-};
+## Step 3 - Understand the basics
+
+Before we dive deep into writing code, there are two concepts you should be familiar with - `StreamVideoClient` and `Call`.
+
+### Client
+
+`StreamVideoClient` is the low level JavaScript client used by the SDK to communicate with the Stream Video service.
+In the case of React Native Video SDK, you will need to provide this client instance to the top level `StreamVideo` component.
+This component will then provide the client instance to all the child components using React Context.
+
+Using client, you can create a call, receive calls, get list of calls etc. This should be ideally done at sign in stage of the application.
+```tsx
const client = new StreamVideoClient({ apiKey, user, token });
-const call = client.call('default', callId);
-call.join({ create: true });
+```
-export default function App() {
- return (
-
-
-
-
-
-
-
- );
-}
+In this example
+- `apiKey` is the API key of your Stream Video application available on [dashboard](https://dashboard.getstream.io/organization/5380/apps)
+- `user` is the user object
+ ```json
+ {
+ "id": "john_smith",
+ "name": "John Smith",
+ "image": "https://getstream.io/random_png/?id=john_smith&name=John+Smith"
+ }
+ ```
+- and `token` is the user token generated by your server-side API. For development purpose, you can use the token generated by the [Token Generator](https://getstream.io/chat/docs/react/token_generator/).
+You can read more information about client authentication on the [Client & Authentication](../../core/client-auth) guide.
-const VideoCallUI = () => {
- const call = useCall();
+:::info
+Alternatively you can also choose to separate client creation and user connection:
- const { useCallCallingState, useParticipantCount } = useCallStateHooks();
- const callingState = useCallCallingState();
- const participantCount = useParticipantCount();
+```tsx
+const client = new StreamVideoClient({ apiKey });
+await client.connectUser(user, token);
+```
+:::
- if (callingState !== CallingState.JOINED) {
- return Loading...;
+### Call
+
+In the context of Stream video/audio service, a `call` refers to an instance of the [`Call` class](https://github.com/GetStream/stream-video-js/blob/main/packages/client/src/Call.ts#L125) and is utilized for performing call-specific actions, such as joining a call, muting participants, leaving a call, and more.
+You will need to supply this `call` instance to the `StreamCall` component.
+
+The following example illustrates how to create a call instance of the `default` type.
+Detailed information about various call types can be found in the [Call Types](../../core/configuring-call-types) guide.
+
+```tsx
+const call = client.call('audio_room', callId);
+```
+
+Subsequently, the `call.join` method can be utilized to enter the call identified by the given unique case-sensitive `callId`.
+For designating roles to call participants, you can specify the role through the `data.members` parameter of the `call.join` method.
+
+```tsx
+// User joins the call
+call.join({
+ create: true, // create the call if it doesn't exist
+ data: {
+ members: [
+ { user_id: 'john_smith' },
+ { user_id: 'jane_doe' }
+ ],
+ custom: { // custom data set on call
+ title: 'React Native test',
+ description: 'Conducting a test of React Native video calls',
+ },
}
+});
+```
+
+## Step 4 - Setup Starter UI
+
+Lets begin by creating a basic UI for our audio room. Normally you would use a navigation library like [React Navigation](https://reactnavigation.org/) to navigate between screens.
+But for this tutorial we'll keep it simple and mock the navigation using a state variable - `activeScreen`.
+Within your tutorial app, create a folder named `src` and create the following files within it:
+
+- `src/HomeScreen.tsx`
+- `src/VideoCallScreen.tsx`
+
+Now copy the following content into the respective files (as mentioned in header):
+
+
+
+
+```tsx title="src/VideoCallScreen.tsx"
+import React from 'react';
+import {Button, StyleSheet, Text, View} from 'react-native';
+
+type Props = {goToHomeScreen: () => void};
+
+export const VideoCallScreen = ({goToHomeScreen}: Props) => {
return (
-
- Call "{call?.id}" has {participantCount} participants
-
+
+ Here we will add Video Calling UI
+
+
);
};
-```
-Let's review what we did in the above code.
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'center',
+ },
+ text: {
+ fontSize: 20,
+ fontWeight: 'bold',
+ marginBottom: 20,
+ textAlign: 'center',
+ },
+});
-#### User setup
+```
-First we create a user object. You typically sync these users via a server side integration from your own backend. Alternatively, you can also use guest or anonymous users.
+
+
-```ts
-import type { User } from '@stream-io/video-react-native-sdk';
+```tsx title="src/HomeScreen.tsx"
+import React from 'react';
+import {View, Text, Button, StyleSheet} from 'react-native';
-const user: User = {
- id: userId,
- name: 'Santhosh',
- image: `https://getstream.io/random_png/?id=${userId}&name=Santhosh`,
+type Props = {
+ goToVideoCallScreen: () => void;
};
+
+export const HomeScreen = ({goToVideoCallScreen}: Props) => {
+ return (
+
+ Welcome to Video Calling Tutorial
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ text: {
+ fontSize: 20,
+ fontWeight: 'bold',
+ marginBottom: 20,
+ textAlign: 'center',
+ },
+});
```
-#### Client setup
+
+
+
+```tsx title="App.tsx"
+import React, {useState} from 'react';
+import {SafeAreaView, StyleSheet} from 'react-native';
+import {HomeScreen} from './src/HomeScreen';
+import {VideoCallScreen} from './src/VideoCallScreen';
-Next we initialize the client by passing the API Key, user and user token.
+export default function App() {
+ const [activeScreen, setActiveScreen] = useState('home');
+ const goToVideoCallScreen = () => setActiveScreen('video-call');
+ const goToHomeScreen = () => setActiveScreen('home');
-```ts
-import { StreamVideoClient } from '@stream-io/video-react-native-sdk';
+ return (
+
+ {activeScreen === 'video-call' ? (
+
+ ) : (
+
+ )}
+
+ );
+}
-const client = new StreamVideoClient({ apiKey, user, token });
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'center',
+ textAlign: 'center',
+ },
+});
```
-#### Create and join call
+
+
-After the user and client are created, we create a call like this:
-```ts
-const call = client.call('default', callId);
-await call.join({ create: true });
-```
+Hit save and you should see on your emulator or device the following UI.
+
+
+
+
+
-As soon as you use `call.join()` the connection for video & audio is setup.
+Also lets store the credentials somewhere in a separate file. Create a file `config.js` in root directory of application next to `App.tsx`.
:::note
-In production grade apps, you'd typically store the `call` instance in a state variable and take care of correctly disposing it.
-Read more in our [Joining and Creating Calls](../../core/joining-and-creating-calls/) guide.
+In actual application you will want to store apiKey in a secure storage or in environment file.
:::
-#### Rendering the UI
+```tsx title="config.ts"
+export const apiKey = 'API_KEY';
+export const userId = 'USER_ID';
+export const userToken = 'USER_TOKEN';
+export const callId = 'CALL_ID';
+```
-Lastly, the UI is rendered by observing the call state through the state hooks (participants and connection states):
+To make this tutorial easier to follow we have generated a user token for you. Please update the credentials in `config.js` with the actual values shown below:
-```tsx
+
+
+## Step 5 - Setup Video Client
+
+Within this configuration, we will establish a `StreamVideoClient` instance and facilitate the user's connection to the Stream Video service.
+This process will be encapsulated within a distinct hook to ensure the app's lifecycle is effectively managed.
+This specific action should ideally be incorporated into the user's sign-in flow.
+
+Create a file named `useCreateVideoClient.ts` within the `src` folder and copy the following content into it:
+
+```tsx title="src/useCreateVideoClient.ts"
+import {useEffect, useState} from 'react';
import {
- useCallStateHooks,
+ StreamVideoClient,
+ User as UserType,
} from '@stream-io/video-react-native-sdk';
+import {apiKey} from '../config';
+
+export const useCreateVideoClient = (user: UserType, token: string) => {
+ const [client, setClient] = useState(null);
+ useEffect(() => {
+ setClient(new StreamVideoClient({apiKey, user, token}));
-// all call state hooks are available in the useCallStateHooks() object
-// your IDE should help you to explore all of them
-const { useCallCallingState, useParticipantCount } = useCallStateHooks();
+ return () => {
+ // Disconnect from websocket when component unmounts
+ client?.disconnectUser();
+ };
+ }, []);
+
+ return client;
+};
-const callingState = useCallCallingState();
-const participantCount = useParticipantCount();
```
-You'll find all relevant state for the call in `call.state` also exposed through a set of SDK provided hooks.
-The documentation on [Call and Participant state](../../core/call-and-participant-state/) explains this in further detail.
+Once this is done, we can use the hook to create the video client and provide it to the `StreamVideo` component.
+`StreamVideo` component is provided by the SDK and it will provide the client instance to all the child components using React Context.
+It needs to go at the top of the component tree.
-#### Credentials
+```tsx title="App.tsx"
+...
+import React, {useState} from 'react';
+import {SafeAreaView, StyleSheet} from 'react-native';
+import {HomeScreen} from './src/HomeScreen';
+import {VideoCallScreen} from './src/VideoCallScreen';
+// highlight-start
+import {useCreateVideoClient} from './src/useCreateVideoClient';
+import {token, userId} from './config';
+import {StreamVideo} from '@stream-io/video-react-native-sdk';
+// highlight-end
-For testing, please update **REPLACE_WITH_API_KEY**, **REPLACE_WITH_TOKEN**, **REPLACE_WITH_USER_ID** and **REPLACE_WITH_CALL_ID** with the actual values shown below::
+export default function App() {
+ ...
+ // highlight-start
+ const client = useCreateVideoClient(
+ {
+ id: userId,
+ name: 'John Malkovich',
+ image: `https://getstream.io/random_png/?id=${userId}&name=John+Malkovich`,
+ },
+ token,
+ );
-
+ if (!client) {
+ return null;
+ }
+ // highlight-end
+
+ return (
+ // highlight-next-line
+
+
+ ...
+
+ // highlight-next-line
+
+ );
+}
+```
-Now when you run the sample app it will connect successfully. The text will say "Call ... has 1 participant" (yourself) like below:
+You wouldn't see any change in the UI at this point, since we haven't joined the call yet.
-![Preview of the initial run](../assets/02-tutorials/01-video-calling/video-call-preview.png)
+## Step 6 - Create & Join a call
-### Step 4 - Joining from the web
+In this step we will create and join a call. Call will be stored in a state variable `call` and it needs to be
+provided to `StreamCall` component. As explained earlier, `StreamCall` component is provided by the SDK and it provides all the necessary hooks for configuring UI around audio room.
+We will explore these hooks later in the tutorial.
-To make this a little more interactive, let's join the call from your browser.
+Open up `src/VideoCallScreen.tsx` and replace it with this code:
-
+```tsx title="src/VideoCallScreen.tsx"
+...
+// highlight-start
+import {Call, StreamCall} from '@stream-io/video-react-native-sdk';
+// highlight-end
+
+...
+
+export const VideoCallScreen = ({goToHomeScreen}: Props) => {
+ // highlight-next-line
+ const [call, setCall] = React.useState(null);
+
+ // highlight-start
+ if (!call) {
+ return (
+
+ Joining call...
+
+ );
+ }
+ // highlight-end
+
+ return (
+ // highlight-next-line
+
+
+ Here we will add Video Calling UI
+
+
+ // highlight-next-line
+
+ );
+};
+```
+
+Now we can join the call with id specified in `config.ts` file. Call id is a case-sensetive unique identifier for a call, which you will need to generate on your server-side API.
+Also as explained earlier in [Understand the Basics](#step-3---understand-the-basics) section, call can be created or accessed using `client.call(...)` method.
+Thus we need access to `client` inside `VideoCallScreen` component. We will use the `useStreamVideoContext` hook to get access to the client instance.
+
+We will put the joining logic inside useEffect hook, so we automatically join the call when user goes to `VideoCallScreen`.
+
+```tsx title="src/VideoCallScreen.tsx"
+// highlight-next-line
+import React, {useEffect} from 'react';
+import {AudioRoom} from './AudioRoom';
+// highlight-next-line
+import {callId} from '../config';
+import {
+ Call,
+ StreamCall,
+ // highlight-next-line
+ useStreamVideoClient,
+} from '@stream-io/video-react-native-sdk';
+
+export const VideoCallScreen = (
+ ...
+) => {
+ const [call, setCall] = React.useState(null);
+ // highlight-next-line
+ const client = useStreamVideoClient();
-In your app, you'll see the text update to 2 participants. Let's keep the browser tab open as you go through the tutorial.
+ // highlight-start
+ useEffect(() => {
+ const joinCall = async () => {
+ if (!client) {
+ return null;
+ }
-### Step 5 - Rendering Video
+ const _call = client.call('default', callId);
+ await _call.join({ create: true });
-In this step, we are going to add the participant's view which displays the participants' video and audio stream and other related info. It will also add buttons that allow the user to control their streaming settings of audio and video.
+ setCall(_call);
+ };
+
+ joinCall();
+ }, [client]);
+ // highlight-end
+
+ ...
+};
+
+```
+
+To enhance the interactivity of this tutorial moving forward, kindly follow these steps:
+
+- Give the app a refresh, then tap the "Join Audio Room" button within your mobile tutorial app.
+- Access the web version of the audio room on your browser by clicking the "Join Call" link provided below, and subsequently, hit the "Join" button.
+
+Currently, you won't have the ability to speak, as we haven't set up the user interface for the video call.
+However, you will be able to view the current user listed among the participants on the web application.
+
+
+
+## Step 7 - Rendering Video UI
+
+In this step, we are going to add the participant's view which displays the participants' video and audio stream and other related info.
+It will also add buttons that allow the user to control their streaming settings of audio and video.
The `CallContent` adds the following things to the UI automatically:
@@ -302,35 +602,248 @@ The `CallContent` adds the following things to the UI automatically:
- Buttons to toggle audio/video and to flip the camera.
- Button to hang up the call.
-Update the `VideoCallUI` component as below:
-
-```tsx title="src/App.tsx"
-// ... omitted imports
-import { CallContent } from '@stream-io/video-react-native-sdk';
+Update the `VideoCallScreen` component as below:
-// ... omitted code
+```tsx title="src/VideoCallScreen.tsx"
+...
+import {
+ ...
+ useStreamVideoClient,
+ // highlight-next-line
+ CallContent,
+} from '@stream-io/video-react-native-sdk';
-const VideoCallUI = () => {
+...
+export const VideoCallScreen = ({goToHomeScreen}: Props) => {
+ ...
return (
- <>
- // highlight-next-line
-
- >
+
+
+ // removed-block-start
+ Here we will add Video Calling UI
+
+ // removed-block-end
+ // added-block-start
+
+ // added-block-end
+
+
);
};
+...
```
Now when you run the app, you'll see your local video in a floating video element and the video from your other browser tab. You should also see the buttons to control the streaming settings. The end result should look like this:
![Preview of the video view](../assets/02-tutorials/01-video-calling/video-call.png)
-### Step 6 - Customizing the UI
+
+## Step 8 - Customizing the UI
You can customize the UI by:
- Building your own UI components (the most flexibility, build anything).
-- Mixing and matching with Stream's UI Components (speeds up how quickly you can build custom video UI).
+- Mixing and matching with Stream's UI Components (speeds up how quickly you can build common video UIs).
+- Theming (basic customization of colors, fonts etc).
+
+You can provide custom component as prop to `CallContent` to customize the UI.
+Example below shows how you can
+
+```tsx title="src/VideoCallScreen.tsx"
+...
+import {
+ ...
+ StreamCall,
+ // highlight-start
+ CallControlProps,
+ HangUpCallButton,
+ ToggleAudioPublishingButton,
+ ToggleVideoPublishingButton,
+ useCall,
+ // highlight-end
+ useStreamVideoClient,
+} from '@stream-io/video-react-native-sdk';
+import {callId} from '../config';
+
+// highlight-start
+const CustomCallControls = (props: CallControlProps) => {
+ const call = useCall();
+ return (
+
+ {
+ console.log('toggle audio publishing button pressed');
+ call?.microphone.toggle();
+ }}
+ />
+ {
+ console.log('toggle video publishing button pressed');
+ call?.camera.toggle();
+ }}
+ />
+
+
+ );
+};
+// highlight-end
+
+type Props = {goToHomeScreen: () => void};
+
+export const VideoCallScreen = ({goToHomeScreen}: Props) => {
+ ...
+
+ return (
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ ...
+ // highlight-start
+ customCallControlsContainer: {
+ position: 'absolute',
+ bottom: 20,
+ paddingVertical: 10,
+ width: '80%',
+ marginHorizontal: 20,
+ flexDirection: 'row',
+ alignSelf: 'center',
+ justifyContent: 'space-around',
+ backgroundColor: 'pink',
+ borderRadius: 10,
+ },
+ // highlight-end
+});
+
+```
+
+![Preview of the video view](../assets/02-tutorials/01-video-calling/video-call-control-customization.png)
+
+All essential call information can be accessed through the SDK's provided hooks, facilitated by the `StreamCall` component.
+You can conveniently access a range of hooks related to the call's state by utilizing the `useCallStateHooks` hook as demonstrated below.
+By utilizing these hooks, you can confidently depend on the most up-to-date state information.
+
+In the following example we will customize the top bar of the CallContent to display
+- current participants in the call
+- name of the dominant speaker
+
+```tsx title="src/VideoCallScreen.tsx"
+import {
+ ...
+ useStreamVideoClient,
+ // highlight-next-line
+ useCallStateHooks,
+} from '@stream-io/video-react-native-sdk';
+
+...
+
+// highlight-start
+const CustomTopView = () => {
+ const {useParticipants, useDominantSpeaker} = useCallStateHooks();
+ const participants = useParticipants();
+ const dominantSpeaker = useDominantSpeaker();
+ return (
+
+
+ Video Call between {participants.map(p => p.name).join(', ')} humans
+
+ Dominant Speaker: {dominantSpeaker?.name}
+
+ );
+};
+// highlight-end
+
+export const VideoCallScreen = ({goToHomeScreen}: Props) => {
+ ...
+ return (
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ ...
+ // highlight-start
+ customCallTopViewContainer: {
+ width: '100%',
+ height: 100,
+ backgroundColor: 'lightblue',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ // highlight-end
+});
+
+```
+
+![Preview of the video view](../assets/02-tutorials/01-video-calling/video-top-view-customization.png)
+
+You can also adjust the style of the underlying UI using the style prop on `StreamVideo` component.
+The complete array of properties that can be themed is available within the [theme](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/theme/theme.ts) file.
+
+```tsx title="App.tsx"
+// highlight-next-line
+import React, {useMemo, useState} from 'react';
+...
+
+export default function App() {
+ ...
+ // Avoid passing inline styles to the component, as it will cause unnecessary re-renders
+ // highlight-start
+ const theme = useMemo(
+ () => ({
+ callControlsButton: {
+ container: {
+ borderRadius: 10,
+ },
+ },
+ hangupCallButton: {
+ container: {
+ backgroundColor: 'blue',
+ },
+ },
+ toggleAudioPublishingButton: {
+ container: {
+ backgroundColor: 'yellow',
+ },
+ },
+ }),
+ [],
+ );
+ // highlight-end
+
+ if (!client) {
+ return null;
+ }
+
+ return (
+ // highlight-next-line
+
+ ...
+
+ );
+...
+```
-You can find many examples on how to build your own custom UI components in our [UI Cookbook docs](../../ui-cookbook/overview/).
+![Preview of the video view](../assets/02-tutorials/01-video-calling/video-style-customization.png)
### Recap
diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/assets/02-tutorials/01-video-calling/home-screen.png b/packages/react-native-sdk/docusaurus/docs/reactnative/assets/02-tutorials/01-video-calling/home-screen.png
new file mode 100644
index 0000000000..44821c1472
Binary files /dev/null and b/packages/react-native-sdk/docusaurus/docs/reactnative/assets/02-tutorials/01-video-calling/home-screen.png differ
diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/assets/02-tutorials/01-video-calling/video-call-control-customization.png b/packages/react-native-sdk/docusaurus/docs/reactnative/assets/02-tutorials/01-video-calling/video-call-control-customization.png
new file mode 100644
index 0000000000..b663bed5a1
Binary files /dev/null and b/packages/react-native-sdk/docusaurus/docs/reactnative/assets/02-tutorials/01-video-calling/video-call-control-customization.png differ
diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/assets/02-tutorials/01-video-calling/video-empty.png b/packages/react-native-sdk/docusaurus/docs/reactnative/assets/02-tutorials/01-video-calling/video-empty.png
new file mode 100644
index 0000000000..e3c3e3bf6b
Binary files /dev/null and b/packages/react-native-sdk/docusaurus/docs/reactnative/assets/02-tutorials/01-video-calling/video-empty.png differ
diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/assets/02-tutorials/01-video-calling/video-style-customization.png b/packages/react-native-sdk/docusaurus/docs/reactnative/assets/02-tutorials/01-video-calling/video-style-customization.png
new file mode 100644
index 0000000000..aa05ca9dc0
Binary files /dev/null and b/packages/react-native-sdk/docusaurus/docs/reactnative/assets/02-tutorials/01-video-calling/video-style-customization.png differ
diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/assets/02-tutorials/01-video-calling/video-top-view-customization.png b/packages/react-native-sdk/docusaurus/docs/reactnative/assets/02-tutorials/01-video-calling/video-top-view-customization.png
new file mode 100644
index 0000000000..d12319d57c
Binary files /dev/null and b/packages/react-native-sdk/docusaurus/docs/reactnative/assets/02-tutorials/01-video-calling/video-top-view-customization.png differ