Introduction
Integrating a Video SDK with Expo (React Native) can be a powerful way to add real-time video communication features to your mobile application. In this guide, we'll walk you through the process of integrating the VideoSDK with your Expo project, step by step. By the end of this tutorial, you'll have a solid understanding of how to set up and configure the VideoSDK, and use its features in your Expo app.
Getting Started
We must use the capabilities that the VideoSDK offers. Before dipping into the implementation steps, ensure you complete the necessary prerequisites.
Create a VideoSDK Account
Go to your VideoSDK dashboard and sign up if you don't have an account. This account gives you access to the required Video SDK token, which acts as an authentication key that allows your application to interact with VideoSDK functionality.
Generate your Auth Token
Visit your VideoSDK dashboard and navigate to the "API Key" section to generate your auth token. This token is crucial in authorizing your application to use VideoSDK features.
For a more visual understanding of the account creation and token generation process, consider referring to the provided tutorial.
Prerequisites and Setup
Make sure your development environment meets the following requirements:
- Node.js v12+
- NPM v6+ (comes installed with newer Node versions)
Project Structure
Directory Structure
root
├── node_modules
├── App.js
├── api.js
Install VideoSDK Config.
It is necessary to set up VideoSDK within your project before going into the details. Installing VideoSDK using NPM or Yarn will depend on the needs of your project.
For NPM
npm install "@videosdk.live/react-native-sdk" "@videosdk.live/react-native-incallmanager"
For Yarn
yarn add "@videosdk.live/react-native-sdk" "@videosdk.live/react-native-incallmanager"
Integrating React Native VideoSDK with Expo
This guide details integrating VideoSDK seamlessly into your Expo project. Expo is a popular framework for building cross-platform mobile applications using React Native.
expo-dev-client
library to run your Expo app with a development build for testing purposes.Creating a New Expo Project
Open your terminal and run the following command to create a new Expo project:
npx create-expo-app my-video-app
- Replace
my-video-app
with your desired project name.
Navigate to your project directory:
cd my-video-app
Installing the VideoSDK
Use the following commands to install the VideoSDK and its dependencies:
npx expo install @videosdk.live/react-native-sdk
npx expo install @videosdk.live/react-native-webrtc
npx expo install @videosdk.live/react-native-incallmanager
npx expo install @videosdk.live/react-native-react-native-foreground-service
npx expo install @videosdk.live/expo-config-plugin
npx expo install @config-plugins/react-native-webrtc
Adding Configuration Plugins
Update your app.json
file to include the configuration plugins:
{
"expo": {
// ...
"plugins": [
"@videosdk.live/expo-config-plugin",
[
"@config-plugins/react-native-webrtc",
{
"cameraPermission": "Allow $(PRODUCT_NAME) to access your camera",
"microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone"
}
]
]
}
}
This adds the @videosdk.live/expo-config-plugin
and @config-plugins/react-native-webrtc
plugins.
The @config-plugins/react-native-webrtc
plugin configuration optionally allows you to customize permission request messages for iOS.
Updating Native Folders
Run the following command to update the native folders with the added plugins:
npx expo prebuild
Troubleshooting: Expo 50+ Compatibility
Expo versions above 50 use event-target-shim@5
, which conflicts with react-native-webrtc
's dependency on event-target-shim@6
. To resolve this:
if the project doesn't have metro.config.js
file, then create a new metro.config.js
file and paste below code.
const { getDefaultConfig } = require("expo/metro-config");
const resolveFrom = require("resolve-from");
/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);
config.resolver.resolveRequest = (context, moduleName, platform) => {
if (
// If the bundle is resolving "event-target-shim" from a module that is part of "react-native-webrtc".
moduleName.startsWith("event-target-shim") &&
context.originModulePath.includes("@videosdk.live/react-native-webrtc")
) {
const updatedModuleName = moduleName.endsWith("/index")
? moduleName.replace("/index", "")
: moduleName;
// Resolve event-target-shim relative to the react-native-webrtc package to use v6.
// React Native requires v5 which is not compatible with react-native-webrtc.
const eventTargetShimPath = resolveFrom(
context.originModulePath,
updatedModuleName
);
return {
filePath: eventTargetShimPath,
type: "sourceFile",
};
}
// Ensure you call the default resolver.
return context.resolveRequest(context, moduleName, platform);
};
module.exports = config;
Registering Services
In your project's main App.js
file, register the VideoSDK services:
import { register } from "@videosdk.live/react-native-sdk";
import { registerRootComponent } from "expo";
// Register VideoSDK services before app component registration
register();
registerRootComponent(App);
export default function App() {}
After installing the VideoSDK in your Expo project, you can start using its features to add real-time video communication to your app. The VideoSDK provides a comprehensive set of APIs and components that allow you to create, join, and manage video meetings, as well as control various aspects of the meeting experience.
Essential Steps to Implement the Video Calling Functionality
Step 1: Get started with api.js
Before moving on, you must create an API request to generate a unique meetingId. You will need an authentication token, which you can create either through the videosdk-rtc-api-server-examples or directly from the VideoSDK Dashboard for developers.
export const token = "<Generated-from-dashbaord>";
// API call to create meeting
export const createMeeting = async ({ token }) => {
const res = await fetch(`https://api.videosdk.live/v2/rooms`, {
method: "POST",
headers: {
authorization: `${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
const { roomId } = await res.json();
return roomId;
};
Step 2: Wireframe App.js with all the components
To build up a wireframe of App.js, you need to use VideoSDK Hooks and Context Providers. VideoSDK provides MeetingProvider, MeetingConsumer, useMeeting, and useParticipant hooks.
First, you need to understand the Context Provider and Consumer. Context is primarily used when some data needs to be accessible by many components at different nesting levels.
- MeetingProvider: This is the Context Provider. It accepts value
config
andtoken
as a prop. The Provider component accepts a value prop to be passed to consuming components that are descendants of this Provider. One Provider can be connected to many consumers. Providers can be nested to override values deeper within the tree. - MeetingConsumer: This is the Context Consumer. All consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes.
- useMeeting: This is the meeting hook API. It includes all the information related to meetings such as join, leave, enable/disable the mic or webcam, etc.
- useParticipant: This is the participant hook API. It is responsible for handling all the events and props related to one particular participant such as name, webcamStream, micStream, etc.
The Meeting Context provides a way to listen for any changes that occur when a participant joins the meeting or makes modifications to their microphone, camera, and other settings.
Begin by making a few changes to the code in the App.js file.
import React, { useState } from "react";
import {
SafeAreaView,
TouchableOpacity,
Text,
TextInput,
View,
FlatList,
} from "react-native";
import {
MeetingProvider,
useMeeting,
useParticipant,
MediaStream,
RTCView,
} from "@videosdk.live/react-native-sdk";
import { createMeeting, token } from "./api";
function JoinScreen(props) {
return null;
}
function ControlsContainer() {
return null;
}
function MeetingView() {
return null;
}
export default function App() {
const [meetingId, setMeetingId] = useState(null);
const getMeetingId = async (id) => {
const meetingId = id == null ? await createMeeting({ token }) : id;
setMeetingId(meetingId);
};
return meetingId ? (
<SafeAreaView style={{ flex: 1, backgroundColor: "#F6F6FF" }}>
<MeetingProvider
config={{
meetingId,
micEnabled: false,
webcamEnabled: true,
name: "Test User",
}}
token={token}
>
<MeetingView />
</MeetingProvider>
</SafeAreaView>
) : (
<JoinScreen getMeetingId={getMeetingId} />
);
}
Step 3: Implement Join Screen
The join screen will serve as a medium to either schedule a new meeting or join an existing one.
function JoinScreen(props) {
const [meetingVal, setMeetingVal] = useState("");
return (
<SafeAreaView
style={{
flex: 1,
backgroundColor: "#F6F6FF",
justifyContent: "center",
paddingHorizontal: 6 * 10,
}}
>
<TouchableOpacity
onPress={() => {
props.getMeetingId();
}}
style={{ backgroundColor: "#1178F8", padding: 12, borderRadius: 6 }}
>
<Text style={{ color: "white", alignSelf: "center", fontSize: 18 }}>
Create Meeting
</Text>
</TouchableOpacity>
<Text
style={{
alignSelf: "center",
fontSize: 22,
marginVertical: 16,
fontStyle: "italic",
color: "grey",
}}
>
---------- OR ----------
</Text>
<TextInput
value={meetingVal}
onChangeText={setMeetingVal}
placeholder={"XXXX-XXXX-XXXX"}
style={{
padding: 12,
borderWidth: 1,
borderRadius: 6,
fontStyle: "italic",
}}
/>
<TouchableOpacity
style={{
backgroundColor: "#1178F8",
padding: 12,
marginTop: 14,
borderRadius: 6,
}}
onPress={() => {
props.getMeetingId(meetingVal);
}}
>
<Text style={{ color: "white", alignSelf: "center", fontSize: 18 }}>
Join Meeting
</Text>
</TouchableOpacity>
</SafeAreaView>
);
}
Step 4: Implement Controls
The next step is to create a ControlsContainer
component to manage features such as Join or leave a Meeting and Enable or Disable the Webcam/Mic.
In this step, the useMeeting
hook is utilized to acquire all the required methods such as join()
, leave()
, toggleWebcam
and toggleMic
.
const Button = ({ onPress, buttonText, backgroundColor }) => {
return (
<TouchableOpacity
onPress={onPress}
style={{
backgroundColor: backgroundColor,
justifyContent: "center",
alignItems: "center",
padding: 12,
borderRadius: 4,
}}
>
<Text style={{ color: "white", fontSize: 12 }}>{buttonText}</Text>
</TouchableOpacity>
);
};
function ControlsContainer({ join, leave, toggleWebcam, toggleMic }) {
return (
<View
style={{
padding: 24,
flexDirection: "row",
justifyContent: "space-between",
}}
>
<Button
onPress={() => {
join();
}}
buttonText={"Join"}
backgroundColor={"#1178F8"}
/>
<Button
onPress={() => {
toggleWebcam();
}}
buttonText={"Toggle Webcam"}
backgroundColor={"#1178F8"}
/>
<Button
onPress={() => {
toggleMic();
}}
buttonText={"Toggle Mic"}
backgroundColor={"#1178F8"}
/>
<Button
onPress={() => {
leave();
}}
buttonText={"Leave"}
backgroundColor={"#FF0000"}
/>
</View>
);
}
ControlsContainer Component
function ParticipantList() {
return null;
}
function MeetingView() {
const { join, leave, toggleWebcam, toggleMic, meetingId } = useMeeting({});
return (
<View style={{ flex: 1 }}>
{meetingId ? (
<Text style={{ fontSize: 18, padding: 12 }}>
Meeting Id :{meetingId}
</Text>
) : null}
<ParticipantList />
<ControlsContainer
join={join}
leave={leave}
toggleWebcam={toggleWebcam}
toggleMic={toggleMic}
/>
</View>
);
}
MeetingView Component
Step 5: Render Participant List
After implementing the controls, the next step is to render the joined participants.
You can get all the joined participants
from the useMeeting
Hook.
function ParticipantView() {
return null;
}
function ParticipantList({ participants }) {
return participants.length > 0 ? (
<FlatList
data={participants}
renderItem={({ item }) => {
return <ParticipantView participantId={item} />;
}}
/>
) : (
<View
style={{
flex: 1,
backgroundColor: "#F6F6FF",
justifyContent: "center",
alignItems: "center",
}}
>
<Text style={{ fontSize: 20 }}>Press Join button to enter meeting.</Text>
</View>
);
}
ParticipantList Component
function MeetingView() {
// Get `participants` from useMeeting Hook
const { join, leave, toggleWebcam, toggleMic, participants } = useMeeting({});
const participantsArrId = [...participants.keys()];
return (
<View style={{ flex: 1 }}>
<ParticipantList participants={participantsArrId} />
<ControlsContainer
join={join}
leave={leave}
toggleWebcam={toggleWebcam}
toggleMic={toggleMic}
/>
</View>
);
}
MeetingView Component
Step 6: Handling Participant's Media
Before Handling the Participant's Media, you need to understand a couple of concepts.
[a] useParticipant
Hook
useParticipant
HookThe useParticipant
hook is responsible for handling all the properties and events of one particular participant who joined the meeting. It will take participantId
as argument.
const { webcamStream, webcamOn, displayName } = useParticipant(participantId);
useParticipant Hook Example
[b] MediaStream API
The MediaStream API is beneficial for adding a MediaTrack to the RTCView
component, enabling the playback of audio or video.
<RTCView
streamURL={new MediaStream([webcamStream.track]).toURL()}
objectFit={"cover"}
style={{
height: 300,
marginVertical: 8,
marginHorizontal: 8,
}}
/>
useParticipant Hook Example
[c] Rendering Participant Media
function ParticipantView({ participantId }) {
const { webcamStream, webcamOn } = useParticipant(participantId);
return webcamOn && webcamStream ? (
<RTCView
streamURL={new MediaStream([webcamStream.track]).toURL()}
objectFit={"cover"}
style={{
height: 300,
marginVertical: 8,
marginHorizontal: 8,
}}
/>
) : (
<View
style={{
backgroundColor: "grey",
height: 300,
justifyContent: "center",
alignItems: "center",
}}
>
<Text style={{ fontSize: 16 }}>NO MEDIA</Text>
</View>
);
}
We hope this guide has been helpful in getting you started with the VideoSDK in your Expo app. If you have any further questions or need additional support, be sure to check out the VideoSDK documentation and community resources.
Conclusion
In this guide, we have covered the steps to integrate the VideoSDK with your Expo (React Native) project. By following these steps, you will be able to add real-time video communication features to your mobile application, allowing your users to connect and collaborate in new ways.
By integrating the VideoSDK, you can provide your users with a powerful and flexible video communication solution that is easy to set up and customize. With features like screen sharing, recording, and moderation tools, you can create a meeting experience that meets the needs of your users and your business.
We hope this guide has been helpful in getting you started with the VideoSDK in your Expo app. If you have any further questions or need additional support, be sure to check out the VideoSDK documentation and community resources.
Remember, VideoSDK offers a generous free tier that includes 10,000 minutes allowing you to experiment and build your video call application without initial investment. Start your free trial today and unlock the potential streaming in your React Native app!