In this blog, we will learn how to create a React Native audio and video calling app using Agora.
Introduction
Create an audio and video calling app in React Native using Agora to improve communication. Follow this guide for step-by-step instructions and code examples.
Implementation
Now we will implement the app, step by step.
Setting Up Your Project with React Native
To set up the environment, check out our previous blog, Getting Started With React Native, where we walk through the entire setup process.
Set Up Agora SDK
1 |
npm install react-native-agora |
Click on “Agora” to learn how to create a project in your Agora account and obtain the App ID and temporary token.
Set Up Navigation
For set-up navigation use React Navigation.
Create the App Components
We’ll start by creating a helper file named helper in the src/helper folder.
1. helper
The getPermission
function handles camera and audio permissions on Android using PermissionsAndroid.requestMultiple
.
1 2 3 4 5 6 7 8 9 10 |
import { PermissionsAndroid, Platform } from 'react-native'; export const getPermission = async () => { if (Platform.OS === 'android') { await PermissionsAndroid.requestMultiple([ PermissionsAndroid.PERMISSIONS.RECORD_AUDIO, PermissionsAndroid.PERMISSIONS.CAMERA, ]); } }; |
2. Setting Up Common Functions For Video And Voice Call
Common functions manage joining, leaving, and handling roles for both video and voice calls.
Initialize Agora Engine
This code initializes the Agora RTC engine for video or voice calls, sets event handlers, and manages permissions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
import { Platform } from 'react-native'; import { createAgoraRtcEngine, IRtcEngine, IRtcEngineEventHandler } from 'react-native-agora'; import { getPermission } from '../../helper/helper'; const appId = 'app_id'; /** * Initialize Agora Engine for Video or Voice Calls */ export const initializeAgoraEngine = async ( agoraEngineRef: React.MutableRefObject<IRtcEngine | undefined>, eventHandlerRef: React.MutableRefObject<IRtcEngineEventHandler | undefined>, showMessage: (msg: string) => void, isVideoCall: boolean, channelName: string ) => { try { if (Platform.OS === 'android') { await getPermission(); } agoraEngineRef.current = createAgoraRtcEngine(); const agoraEngine = agoraEngineRef.current; // Event Handlers eventHandlerRef.current = { onJoinChannelSuccess: () => { showMessage(`Successfully joined ${isVideoCall ? 'video' : 'voice'} channel: ${channelName}`); }, onUserJoined: (_connection, uid) => { showMessage(`Remote user ${uid} joined`); }, onUserOffline: (_connection, uid) => { showMessage(`Remote user ${uid} left the channel`); }, }; // Register Event Handlers agoraEngine.registerEventHandler(eventHandlerRef.current); // Initialize Agora Engine agoraEngine.initialize({ appId }); // Enable video if it's a video call if (isVideoCall) { agoraEngine.enableVideo(); } } catch (e) { console.error('Error initializing Agora Engine:', e); } }; |
Join Agora Channel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
import { IRtcEngine } from 'react-native-agora'; import { ChannelProfileType, ClientRoleType } from 'react-native-agora'; /** * Join a Channel for Video or Voice Calls */ export const joinAgoraChannel = async ( agoraEngineRef: React.MutableRefObject<IRtcEngine | undefined>, isHost: boolean, setJoined: (value: boolean) => void, showMessage: (msg: string) => void, isVideoCall: boolean, channelName: string ) => { try { const token = 'temp_token'; const uid = 0; const agoraEngine = agoraEngineRef.current; if (!agoraEngine) { console.error('Agora engine not initialized'); return; } const clientRoleType = isHost ? ClientRoleType.ClientRoleBroadcaster : ClientRoleType.ClientRoleAudience; await agoraEngine.joinChannel(token, channelName, uid, { channelProfile: ChannelProfileType.ChannelProfileCommunication, clientRoleType, publishMicrophoneTrack: isHost, publishCameraTrack: isHost && isVideoCall, autoSubscribeAudio: true, autoSubscribeVideo: isVideoCall, }); if (isHost && isVideoCall) { agoraEngine.startPreview(); } setJoined(true); showMessage(`Joined ${isVideoCall ? 'video' : 'voice'} channel as ${isHost ? 'host' : 'audience'}`); } catch (e) { console.error('Error joining channel:', e); } }; |
Leave Agora Channel
This code leaves the Agora channel, stops the connection, and updates the state to reflect the user has left. It also displays a message confirming the action.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import { IRtcEngine } from 'react-native-agora'; /** * Leave a Channel */ export const leaveAgoraChannel = ( agoraEngineRef: React.MutableRefObject<IRtcEngine | undefined>, setJoined: (value: boolean) => void, showMessage: (msg: string) => void ) => { try { const agoraEngine = agoraEngineRef.current; if (!agoraEngine) { console.error('Agora engine not initialized'); return; } agoraEngine.leaveChannel(); setJoined(false); showMessage('Left the channel'); } catch (e) { console.error('Error leaving channel:', e); } }; |
3. Video Call UI
This component shows a video call UI with buttons to join/leave, switch between host and audience, and display local/remote videos. It handles actions and updates using props.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
import React from 'react'; import { SafeAreaView, ScrollView, Text, View, Switch, GestureResponderEvent } from 'react-native'; import { RtcSurfaceView } from 'react-native-agora'; import styles from './styles'; // Define props type interface VideoCallUIProps { isJoined: boolean; isHost: boolean; remoteUid: number; message: string; onJoin: (event: GestureResponderEvent) => void; onLeave: (event: GestureResponderEvent) => void; onToggleHost: (value: boolean) => void; } const VideoCallUI: React.FC<VideoCallUIProps> = ({ isJoined, isHost, remoteUid, message, onJoin, onLeave, onToggleHost, }) => { return ( <SafeAreaView style={styles.main}> <Text style={styles.head}>Agora Video SDK Quickstart</Text> {/* Buttons for Join/Leave Channel */} <View style={styles.btnContainer}> <Text onPress={onJoin} style={styles.button}> Join Channel </Text> <Text onPress={onLeave} style={styles.button}> Leave Channel </Text> </View> {/* Host/Audience Toggle */} <View style={styles.btnContainer}> <Text>Audience</Text> <Switch onValueChange={onToggleHost} value={isHost} /> <Text>Host</Text> </View> {/* Video Feeds and Message */} <ScrollView contentContainerStyle={styles.scrollContainer}> {isJoined && <RtcSurfaceView canvas={{ uid: 0 }} style={styles.videoView} />} {remoteUid !== 0 && <RtcSurfaceView canvas={{ uid: remoteUid }} style={styles.videoView} />} <Text>{message}</Text> </ScrollView> </SafeAreaView> ); }; export default VideoCallUI; |
4. Video Call
The Video Call screen allows users to join or leave a channel, toggle between Host and Audience roles, and view live video streams. It uses Agora’s SDK for real-time video and event handling.
Here’s a basic example of what your index.tsx might look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
import React, { useRef, useState, useEffect } from 'react'; import { initializeAgoraEngine } from './initializeAgoraEngine'; import { joinAgoraChannel } from './joinAgoraChannel'; import { leaveAgoraChannel } from './leaveAgoraChannel'; import VideoCallUI from './VideoCallUI'; const channelName = 'test'; const VideoCall: React.FC = () => { const agoraEngineRef = useRef<any>(null); const eventHandlerRef = useRef<any>(null); const [isJoined, setIsJoined] = useState(false); const [isHost, setIsHost] = useState(true); const [remoteUid, setRemoteUid] = useState(0); const [message, setMessage] = useState(''); const showMessage = (msg: string) => { setMessage(msg); }; useEffect(() => { initializeAgoraEngine( agoraEngineRef, eventHandlerRef, showMessage, true, // isVideoCall channelName ); return () => { agoraEngineRef.current?.release(); }; }, []); const handleJoin = () => { joinAgoraChannel( agoraEngineRef, isHost, setIsJoined, showMessage, true, // isVideoCall channelName ); }; const handleLeave = () => { leaveAgoraChannel(agoraEngineRef, setIsJoined, showMessage); }; const handleToggleHost = (value: boolean) => { setIsHost(value); if (isJoined) { leaveAgoraChannel(agoraEngineRef, setIsJoined, showMessage); } }; return ( <VideoCallUI isJoined={isJoined} isHost={isHost} remoteUid={remoteUid} message={message} onJoin={handleJoin} onLeave={handleLeave} onToggleHost={handleToggleHost} /> ); }; export default VideoCall; |
5. Voice Call UI
This component manages the voice call UI, displaying buttons for joining or leaving the channel and showing status messages based on call state.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
import React from 'react'; import { SafeAreaView, ScrollView, Text, View } from 'react-native'; import styles from './styles'; interface VoiceCallUIProps { isJoined: boolean; remoteUid: number; message: string; onJoin: () => void; onLeave: () => void; } const VoiceCallUI: React.FC<VoiceCallUIProps> = ({ isJoined, remoteUid, message, onJoin, onLeave }) => { return ( <SafeAreaView style={styles.main}> <Text style={styles.head}>Agora Voice Calling Quickstart</Text> {/* Join/Leave Buttons */} <View style={styles.btnContainer}> <Text onPress={onJoin} style={styles.button}> Join Channel </Text> <Text onPress={onLeave} style={styles.button}> Leave Channel </Text> </View> {/* Status and Messages */} <ScrollView style={styles.scroll} contentContainerStyle={styles.scrollContainer}> {isJoined ? ( <Text>Local user uid: {remoteUid || 'Unknown'}</Text> ) : ( <Text>Join a channel</Text> )} <Text>{message}</Text> </ScrollView> </SafeAreaView> ); }; export default VoiceCallUI; |
6. Voice Call
The Voice Call screen allows users to join or leave channels and switch between host and audience roles with Agora’s SDK. It manages real-time audio communication.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
import React, { useRef, useState, useEffect } from 'react'; import { initializeAgoraEngine } from './initializeAgoraEngine'; import { joinAgoraChannel } from './joinAgoraChannel'; import { leaveAgoraChannel } from './leaveAgoraChannel'; import VoiceCallUI from './VoiceCallUI'; const channelName = 'test'; // Define your channel name here const VoiceCall: React.FC = () => { const agoraEngineRef = useRef<any>(null); // Agora RTC Engine reference const eventHandlerRef = useRef<any>(null); // Event handler reference const [isJoined, setIsJoined] = useState(false); const [isHost, setIsHost] = useState(true); // Assuming the user is a host by default const [message, setMessage] = useState(''); const showMessage = (msg: string) => setMessage(msg); useEffect(() => { // Initialize the Agora engine for a voice call (isVideoCall: false) initializeAgoraEngine(agoraEngineRef, eventHandlerRef, showMessage, false, channelName); // Clean up on component unmount return () => { if (agoraEngineRef.current) { agoraEngineRef.current.unregisterEventHandler(eventHandlerRef.current); agoraEngineRef.current.release(); } }; }, []); const handleJoin = () => { // Join the channel as a host or audience for voice call joinAgoraChannel(agoraEngineRef, isHost, setIsJoined, showMessage, false, channelName); }; const handleLeave = () => { // Leave the channel leaveAgoraChannel(agoraEngineRef, setIsJoined, showMessage); }; const handleToggleHost = (value: boolean) => { setIsHost(value); if (isJoined) { // If already joined, leave the current channel before switching roles leaveAgoraChannel(agoraEngineRef, setIsJoined, showMessage); } }; return ( <VoiceCallUI isJoined={isJoined} isHost={isHost} message={message} onJoin={handleJoin} onLeave={handleLeave} onToggleHost={handleToggleHost} /> ); }; export default VoiceCall; |
7. Home Screen
The Home Screen provides navigation options to start a video or voice call using Agora’s communication features. It welcomes users with a title and a brief description.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import React from 'react'; import { View, Text, TouchableOpacity } from 'react-native'; import styles from './styles'; export default function HomeScreen({ navigation }) { return ( <View style={styles.container}> {/* Title Section */} <Text style={styles.title}>Welcome to Agora Call App</Text> <Text style={styles.subtitle}> Experience seamless communication with Agora's Video and Voice call features. </Text> {/* Buttons Section */} <TouchableOpacity style={styles.button} onPress={() => navigation.navigate('VideoCall')} > <Text style={styles.buttonText}>Start Video Call</Text> </TouchableOpacity> <TouchableOpacity style={[styles.button, styles.voiceButton]} onPress={() => navigation.navigate('VoiceCall')} > <Text style={styles.buttonText}>Start Voice Call</Text> </TouchableOpacity> </View> ); } |
8. Style
We have created separate style files for the Video Call, Voice Call, and Home Screen components.
style file for Video Call and Voice Call
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import { StyleSheet } from "react-native"; export default StyleSheet.create({ button: { paddingHorizontal: 25, paddingVertical: 4, fontWeight: 'bold', color: '#ffffff', backgroundColor: '#0055cc', margin: 5, }, main: { flex: 1, alignItems: 'center' }, scroll: { flex: 1, backgroundColor: '#ddeeff', width: '100%' }, scrollContainer: { alignItems: 'center' }, videoView: { width: '90%', height: 200 }, btnContainer: { flexDirection: 'row', justifyContent: 'center' }, head: { fontSize: 20 }, info: { backgroundColor: '#ffffe0', paddingHorizontal: 8, color: '#0000ff' }, }); |
style file for Home Screen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
import { StyleSheet } from "react-native"; export default StyleSheet.create({ container: { flex: 1, backgroundColor: '#e3f2fd', alignItems: 'center', justifyContent: 'center', padding: 20, }, title: { fontSize: 24, fontWeight: 'bold', color: '#0d47a1', marginBottom: 10, }, subtitle: { fontSize: 16, color: '#37474f', textAlign: 'center', marginBottom: 30, }, button: { backgroundColor: '#1e88e5', padding: 15, borderRadius: 10, marginBottom: 15, width: '80%', alignItems: 'center', }, voiceButton: { backgroundColor: '#8e24aa', }, buttonText: { fontSize: 16, fontWeight: '600', color: '#ffffff', }, }); |
9. Route
The Route component sets up navigation using a stack navigator with three screens: Home, Video Call, and Voice Call. It initializes the Home screen as the default and provides titles for each screen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
import React from 'react'; import { createStackNavigator } from '@react-navigation/stack'; import { NavigationContainer } from '@react-navigation/native'; import HomeScreen from '../screens/HomeScreen/index'; import VideoCall from '../screens/VideoCall/index'; import VoiceCall from '../screens/VoiceCall/index'; // Define the type for the navigation stack export type RootStackParamList = { Home: undefined; VideoCall: undefined; VoiceCall: undefined; }; const Stack = createStackNavigator<RootStackParamList>(); const Route: React.FC = () => { return ( <NavigationContainer> <Stack.Navigator initialRouteName="Home"> <Stack.Screen name="Home" component={HomeScreen} options={{ title: 'Home Screen' }} /> <Stack.Screen name="VideoCall" component={VideoCall} options={{ title: 'Video Call' }} /> <Stack.Screen name="VoiceCall" component={VoiceCall} options={{ title: 'Voice Call' }} /> </Stack.Navigator> </NavigationContainer> ); }; export default Route; |
10. App
The App component renders the Route component, which handles navigation between screens.
1 2 3 4 5 6 7 8 |
import React from 'react'; import Route from './src/navigation/Route'; const App: React.FC = () => { return <Route />; }; export default App; |
Now save or reload your react native app to view changes.
Here is the Output of the project
Create Audio & Video Calling App Using Agora.
Use the Agora SDK to integrate audio and video calling features into your app. It provides real-time communication APIs for seamless implementation across platforms.
Conclusion
Thanks for reading this blog.
Please check my other blogs here.