React Native Video Calling App
Tags: react, reactnative
Published: 2021-08-10 07:02:39

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fo48q1obyopfruujlhc2.png)
 

**What you need:**
* A Twilio Account 
* Server-side implementation (node.js)
* Client-side implementation with the module — react-native-twilio-video-webrtc 

**Twiilio Account**
We will be using Twilio, so you should signup [here](https://www.twilio.com/try-twilio) and retrieve the essential keys -  API KEY SID, ACCOUNT SID, API KEY SECRET.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xvhufl28tfn9qpbs1q0r.png)
 

**After that, navigate to Settings => API Keys**

click add a new api key, for **type**, select the dropdown and choose **main**.
The api secret will only be shown once. You should copy and paste somewhere safe as you will need all keys for server-side implementation.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ct30rvplczib6lyntn8d.png)
 

**Server-side implementation**

The step here is quite simple. Implement ur regular node express and add this route: 

```
import 'dotenv/config';
import express from 'express';
import twilio from 'twilio';

const AccessToken = twilio.jwt.AccessToken;
const VideoGrant = AccessToken.VideoGrant;
const app = express();

app.get('/getToken', (req, res) => {
if (!req.query || !req.query.room || !req.query.username) {
 return res.status(400).send('username and room parameter is required');
}
const accessToken = new AccessToken(
    process.env.ACCOUNT_SID,
    process.env.API_KEY_SID,
    process.env.API_KEY_SECRET
 ); // Set the Identity of this token
 const grant = new VideoGrant();
 
  accessToken.identity = req.query.username;// Grant access to Video
  grant.room = req.query.room;
  
  accessToken.addGrant(grant); // Serialize the token as a JWT
  const jwt = accessToken.toJwt();
  return res.send(jwt);
  });
```

For this endpoint, we get a username as string and a room as string in the body of the query. We create a room and add the user to that room awaiting other participants.

**Client-side implementation**

Now we can add twilio video webrtc module to our project

`yarn add https://github.com/blackuy/react-native-twilio-video-webrtc`

Then, you follow the simple installation instructions [here](https://github.com/blackuy/react-native-twilio-video-webrtc#install-node-package) for both android and iOS. 




```
import React, { useState, useRef } from "react";
import {
 Alert,
 AppRegistry,
 StyleSheet,
 Text,
 TextInput,
 View,
 Button,
 PermissionsAndroid,
 Platform,
 TouchableOpacity,
} from "react-native";

import {
 TwilioVideoLocalView,
 TwilioVideoParticipantView,
 TwilioVideo,
} from "react-native-twilio-video-webrtc";

import styleSheet from "./styles";

const styles = StyleSheet.create(styleSheet);

const App = (props) => {
 const [isAudioEnabled, setIsAudioEnabled] = useState(true);
 const [isVideoEnabled, setIsVideoEnabled] = useState(true);
 const [status, setStatus] = useState("disconnected");
 const [participants, setParticipants] = useState(new Map());
 const [videoTracks, setVideoTracks] = useState(new Map());
 const [room, setRoom] = useState("");
 const [username, setUsername] = useState("");
 const twilioVideo = useRef(null);

 const fetchToken = async () => {
 try {
 const res = await fetch(
 `https://<your_base_url>/getToken?username=${username}&room=${room}`
      );
 if (!res.ok) {
 console.log("error", error);
 Alert.alert("API not available");
 return null;
      }
 const jwt = await res.text();
 return jwt;
    } catch (error) {
 console.log("error", error);
 Alert.alert("An Error occurred");
 return null;
    }
  };

 const _onConnectButtonPress = async () => {
 if (Platform.OS === "android") {
 await _requestAudioPermission();
 await _requestCameraPermission();
    }
 const token = await fetchToken();
 if (!token) {
 return;
    }
 twilioVideo.current.connect({
 accessToken: token,
 enableNetworkQualityReporting: true,
 dominantSpeakerEnabled: true,
    });
 setStatus("connecting");
  };

 const _onEndButtonPress = () => {
 twilioVideo.current.disconnect();
  };

 const _onMuteButtonPress = () => {
 twilioVideo.current
      .setLocalAudioEnabled(!isAudioEnabled)
      .then((isEnabled) => setIsAudioEnabled(isEnabled));
  };

 const _onFlipButtonPress = () => {
 twilioVideo.current.flipCamera();
  };

 const _onRoomDidConnect = () => {
 setStatus("connected");
  };

 const _onRoomDidDisconnect = ({ error }) => {
 console.log("ERROR: ", error);

 setStatus("disconnected");
  };

 const _onRoomDidFailToConnect = (error) => {
 console.log("ERROR: ", error);

 setStatus("disconnected");
  };

 const _onParticipantAddedVideoTrack = ({ participant, track }) => {
 console.log("onParticipantAddedVideoTrack: ", participant, track);

 setVideoTracks(
 new Map([
        ...videoTracks,
        [
 track.trackSid,
          { participantSid: participant.sid, videoTrackSid: track.trackSid },
        ],
      ])
    );
  };

 const _onParticipantRemovedVideoTrack = ({ participant, track }) => {
 console.log("onParticipantRemovedVideoTrack: ", participant, track);

 const videoTracks = new Map(videoTracks);
 videoTracks.delete(track.trackSid);

 setVideoTracks(videoTracks);
  };

 const _onNetworkLevelChanged = ({ participant, isLocalUser, quality }) => {
 console.log(
 "Participant",
 participant,
 "isLocalUser",
 isLocalUser,
 "quality",
 quality
    );
  };

 const _onDominantSpeakerDidChange = ({ roomName, roomSid, participant }) => {
 console.log(
 "onDominantSpeakerDidChange",
 `roomName: ${roomName}`,
 `roomSid: ${roomSid}`,
 "participant:",
 participant
    );
  };

 const _requestAudioPermission = () => {
 return PermissionsAndroid.request(
 PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
      {
 title: "Need permission to access microphone",
 message:
 "To run this demo we need permission to access your microphone",
 buttonNegative: "Cancel",
 buttonPositive: "OK",
      }
    );
  };

 const _requestCameraPermission = () => {
 return PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA, {
 title: "Need permission to access camera",
 message: "To run this demo we need permission to access your camera",
 buttonNegative: "Cancel",
 buttonPositive: "OK",
    });
  };

 return (
 <View style={styles.container}>
 {status === "disconnected" && (
 <View>
 <Text style={styles.welcome}>React Native Twilio Video</Text>
 <TextInput
 style={styles.input}
 autoCapitalize="none"
 value={username}
 onChangeText={(text) => setUsername(text)}
 />
 <TextInput
 style={styles.input}
 autoCapitalize="none"
 value={room}
 onChangeText={(text) => setRoom(text)}
 />
 <Button
 title="Connect"
 style={styles.button}
 onPress={_onConnectButtonPress}
 ></Button>
 </View>
      )}

 {(status === "connected" || status === "connecting") && (
 <View style={styles.callContainer}>
 {status === "connected" && (
 <View style={styles.remoteGrid}>
 {Array.from(videoTracks, ([trackSid, trackIdentifier]) => {
 return (
 <TwilioVideoParticipantView
 style={styles.remoteVideo}
 key={trackSid}
 trackIdentifier={trackIdentifier}
 />
                );
              })}
 </View>
          )}
 <View style={styles.optionsContainer}>
 <TouchableOpacity
 style={styles.optionButton}
 onPress={_onEndButtonPress}
 >
 <Text style={{ fontSize: 12 }}>End</Text>
 </TouchableOpacity>
 <TouchableOpacity
 style={styles.optionButton}
 onPress={_onMuteButtonPress}
 >
 <Text style={{ fontSize: 12 }}>
 {isAudioEnabled ? "Mute" : "Unmute"}
 </Text>
 </TouchableOpacity>
 <TouchableOpacity
 style={styles.optionButton}
 onPress={_onFlipButtonPress}
 >
 <Text style={{ fontSize: 12 }}>Flip</Text>
 </TouchableOpacity>
 <TwilioVideoLocalView enabled={true} style={styles.localVideo} />
 </View>
 </View>
      )}

 <TwilioVideo
 ref={twilioVideo}
 onRoomDidConnect={_onRoomDidConnect}
 onRoomDidDisconnect={_onRoomDidDisconnect}
 onRoomDidFailToConnect={_onRoomDidFailToConnect}
 onParticipantAddedVideoTrack={_onParticipantAddedVideoTrack}
 onParticipantRemovedVideoTrack={_onParticipantRemovedVideoTrack}
 onNetworkQualityLevelsChanged={_onNetworkLevelChanged}
 onDominantSpeakerDidChange={_onDominantSpeakerDidChange}
 />
 </View>
  );
};

export default App;
```



copy and paste the above code in ur react native project, enter a room and a username and hit the connect button to start your video call.
Oh. and make sure you have at least two devices to test and see that it actually works.

After implementing a a basic video call in react native using Twilio , implementing both server-side and client side, the next step can be to add advanced features and help improve your skills like building a **WhatsApp clone** which include standard calling features such as:
Ringing another participant or multiple participant video calling rooms, group calls, managing and displaying participant status such as busy, declined call or not answered call. 

You should checkout [QuickComponent](https://www.quickcomponent.com) for a proper Video calling app where all this features has been added. You find the React Native [WhatsApp Clone](https://www.quickcomponent.com/react-native-whatsapp-clone) previously mentioned and more React native apps like, [Dating app](https://www.quickcomponent.com/react-native-dating-app), [UberEats clone](https://www.quickcomponent.com/react-native-uber-eats-clone), and more with complete video calling feature.