Creating Rn Video Calling App React Native Webrtc

Build real-time video communication features in React Native with the react-native-webrtc package. A comprehensive guide covering installation, peer connections, signaling servers, and production deployment.

Building Real-Time Video Communication in React Native

WebRTC (Web Real-Time Communication) is an open-source technology that enables peer-to-peer communication directly between devices, making it ideal for video calling, audio streaming, and real-time data transfer. In the React Native ecosystem, the react-native-webrtc package bridges native WebRTC implementations with JavaScript, providing a performant solution for mobile video communication.

The peer-to-peer architecture eliminates the need for media servers in most scenarios, reducing infrastructure costs and latency. The protocol handles NAT traversal through STUN and TURN servers, ensuring connections work across different network configurations. Built-in support for adaptive bitrate streaming means video quality automatically adjusts based on available bandwidth, providing a smooth user experience even on variable connections.

The react-native-webrtc package currently uses WebRTC M124 revision, supporting Android (armeabi-v7a, arm64-v8a, x86, x86_64), iOS (arm64, x86_64), and tvOS platforms. The implementation follows the Unified Plan SDP format, which has become the standard for modern WebRTC applications and supports advanced features like simulcast for multiple video quality streams.

Developers implement WebRTC in React Native for diverse applications including video calling, voice chat, live streaming, screen sharing, gaming communication, and telehealth platforms. This guide covers everything from initial setup to production-ready implementation, enabling you to create robust video communication features for both iOS and Android platforms.

If you're building a mobile application that requires real-time communication, understanding WebRTC fundamentals is essential for delivering a seamless user experience.

Key WebRTC Capabilities

Peer-to-Peer Architecture

Direct device-to-device communication eliminates media server costs and reduces latency for real-time interaction.

NAT Traversal

STUN and TURN servers handle connections behind firewalls, ensuring reliable connectivity across network configurations.

Adaptive Quality

Automatic bitrate adjustment based on available bandwidth maintains smooth video despite changing network conditions.

Multi-Platform Support

Works on iOS, Android, and tvOS with consistent APIs across all React Native platforms.

Installation and Platform Setup

Getting react-native-webrtc running requires proper configuration on each target platform. The installation process differs between iOS and Android, with each requiring specific permissions and configuration files.

Basic Package Installation

The react-native-webrtc package installs through standard React Native package managers:

# Using yarn
yarn add react-native-webrtc

# Using npm
npm install react-native-webrtc

# Using pnpm
pnpm install react-native-webrtc

The package includes native WebRTC libraries compiled for each platform, along with JavaScript bindings that expose the WebRTC API to your React Native code.

iOS Configuration

iOS requires specific permission declarations in the Info.plist file to access the camera and microphone:

<key>NSCameraUsageDescription</key>
<string>This app needs access to camera for video calls</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to microphone for voice calls</string>

After adding these permissions, run pod install to integrate the WebRTC native libraries:

cd ios && pod install

The pod install process downloads the WebRTC.framework and configures your project to link against it. This step is essential for iOS builds to include the necessary native code.

Android Configuration

Android requires permissions in the AndroidManifest.xml file:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

If your application uses code obfuscation through ProGuard, add rules to preserve the WebRTC class names:

-keep class org.webrtc.** { *; }
-dontwarn org.webrtc.**
-keep class com.oney.WebRTCModule.** { *; }

These rules prevent ProGuard from stripping essential WebRTC classes during the build process. Proper platform configuration is critical for a reliable React Native development project.

Core WebRTC Concepts and Implementation

Understanding the WebRTC connection flow is essential for building reliable video calling applications.

The WebRTC Connection Flow

WebRTC connections follow a specific sequence that coordinates session establishment between devices:

  1. Get User Media: Access the device's camera and microphone
  2. Create Peer Connection: Initialize the WebRTC session manager
  3. Create Offer/Answer: Exchange session descriptions between peers
  4. Exchange ICE Candidates: Share network path information
  5. Establish Connection: Media streams flow directly between peers

This flow requires a signaling server to coordinate the initial connection, though the actual media travels peer-to-peer. The signaling server handles offer/answer exchange and ICE candidate distribution but doesn't handle media traffic, keeping costs low and latency minimal.

Creating Peer Connections

import { RTCPeerConnection } from 'react-native-webrtc';

const createPeerConnection = () => {
 const configuration = {
 iceServers: [
 { urls: 'stun:stun.l.google.com:19302' },
 { urls: 'stun:stun1.l.google.com:19302' },
 {
 urls: 'turn:your-turn-server.com:3478',
 username: 'your-username',
 credential: 'your-password',
 },
 ],
 iceCandidatePoolSize: 10,
 };

 const peerConnection = new RTCPeerConnection(configuration);

 peerConnection.addEventListener('icecandidate', (event) => {
 if (event.candidate) {
 sendIceCandidate(event.candidate);
 }
 });

 peerConnection.addEventListener('addstream', (event) => {
 setRemoteStream(event.stream);
 });

 return peerConnection;
};

STUN servers, like Google's public servers, help peers determine their public IP addresses and ports. TURN servers relay traffic when direct peer-to-peer connection fails, which commonly occurs with symmetric NATs or restrictive firewalls. Production applications should include at least one TURN server to ensure connectivity in all network environments. Building robust peer connection handling requires expertise in backend API development for the signaling infrastructure.

Getting User Media

import { mediaDevices } from 'react-native-webrtc';

const getUserMedia = async () => {
 try {
 const stream = await mediaDevices.getUserMedia({
 audio: true,
 video: {
 width: { min: 640, ideal: 1280, max: 1920 },
 height: { min: 360, ideal: 720, max: 1080 },
 frameRate: { min: 15, ideal: 30, max: 60 },
 facingMode: 'user',
 },
 });
 return stream;
 } catch (error) {
 console.error('Error accessing media devices:', error);
 throw error;
 }
};

The facingMode constraint controls which camera the device uses. Setting it to 'user' activates the front-facing camera, which is typical for video calling. Setting it to 'environment' selects the rear camera for scenarios like document scanning or AR applications.

Building a Signaling Server

WebRTC requires a signaling mechanism to exchange session descriptions and ICE candidates. WebSocket connections provide a common approach for real-time signaling:

class SignalingService {
 constructor(url) {
 this.ws = new WebSocket(url);
 this.listeners = {};

 this.ws.onmessage = (event) => {
 const data = JSON.parse(event.data);
 this.emit(data.type, data);
 };
 }

 on(event, callback) {
 if (!this.listeners[event]) {
 this.listeners[event] = [];
 }
 this.listeners[event].push(callback);
 }

 emit(event, data) {
 if (this.listeners[event]) {
 this.listeners[event].forEach(callback => callback(data));
 }
 }

 send(type, data) {
 this.ws.send(JSON.stringify({ type, ...data }));
 }

 sendOffer(offer, roomId) {
 this.send('offer', { offer, roomId });
 }

 sendAnswer(answer, roomId) {
 this.send('answer', { answer, roomId });
 }

 sendIceCandidate(candidate, roomId) {
 this.send('ice-candidate', { candidate, roomId });
 }
}

The signaling server handles room management, allowing multiple participants to connect and communicate. Messages are JSON-serialized for transmission, with a type field indicating the message purpose. The signaling server typically coordinates the initial connection but doesn't handle media traffic once peers are connected.

Production signaling servers require scalability features to handle multiple concurrent connections. Redis integration enables horizontal scaling by sharing room state across multiple signaling server instances, which is essential for applications serving a large number of concurrent video calls.

For applications requiring real-time communication features, implementing a robust signaling infrastructure is as critical as the WebRTC implementation itself. Many teams work with our backend API development services to build scalable signaling infrastructure.

Building a Complete Video Calling Component

Creating production-ready video calling requires managing complex state and coordinating WebRTC operations. A custom React hook encapsulates video call logic, providing a clean interface for components.

Custom Hook for Video Call Management

export const useVideoCall = (roomId, signalingService) => {
 const [localStream, setLocalStream] = useState(null);
 const [remoteStream, setRemoteStream] = useState(null);
 const [isConnected, setIsConnected] = useState(false);
 const [isMuted, setIsMuted] = useState(false);
 const [isVideoEnabled, setIsVideoEnabled] = useState(true);

 const peerConnection = useRef(null);

 useEffect(() => {
 initializeCall();
 setupSignalingListeners();
 return () => cleanup();
 }, [roomId]);

 const initializeCall = async () => {
 try {
 const stream = await getUserMedia();
 setLocalStream(stream);
 peerConnection.current = createPeerConnection();
 stream.getTracks().forEach(track => {
 peerConnection.current.addTrack(track, stream);
 });
 } catch (error) {
 console.error('Failed to initialize call:', error);
 }
 };

 const setupSignalingListeners = () => {
 signalingService.on('offer', handleOffer);
 signalingService.on('answer', handleAnswer);
 signalingService.on('ice-candidate', handleIceCandidate);
 };

 const toggleMute = () => {
 if (localStream) {
 localStream.getAudioTracks().forEach(track => {
 track.enabled = isMuted;
 });
 setIsMuted(!isMuted);
 }
 };

 const toggleVideo = () => {
 if (localStream) {
 localStream.getVideoTracks().forEach(track => {
 track.enabled = !isVideoEnabled;
 });
 setIsVideoEnabled(!isVideoEnabled);
 }
 };

 const cleanup = () => {
 if (localStream) {
 localStream.getTracks().forEach(track => track.stop());
 }
 if (peerConnection.current) {
 peerConnection.current.close();
 }
 setLocalStream(null);
 setRemoteStream(null);
 setIsConnected(false);
 };

 return {
 localStream, remoteStream, isConnected, isMuted, isVideoEnabled,
 toggleMute, toggleVideo, endCall: cleanup,
 };
};

This hook manages the complete call lifecycle, from initialization through cleanup. It handles the complexity of WebRTC state management while providing simple controls for mute, video toggle, and call termination.

Video Call UI Component

const VideoCallScreen = ({ route }) => {
 const { roomId } = route.params;
 const signalingService = new SignalingService('ws://your-server.com');
 const {
 localStream, remoteStream, isConnected, isMuted, isVideoEnabled,
 toggleMute, toggleVideo, endCall,
 } = useVideoCall(roomId, signalingService);

 return (
 <View style={styles.container}>
 {remoteStream ? (
 <RTCView style={styles.remoteVideo} streamURL={remoteStream.toURL()} objectFit="cover" />
 ) : (
 <View style={styles.waitingContainer}>
 <Text>{isConnected ? 'Connected' : 'Waiting...'}</Text>
 </View>
 )}

 {localStream && (
 <RTCView style={styles.localVideo} streamURL={localStream.toURL()} objectFit="cover" mirror={true} />
 )}

 <View style={styles.controls}>
 <TouchableOpacity onPress={toggleMute}>
 <Icon name={isMuted ? 'mic-off' : 'mic'} />
 </TouchableOpacity>
 <TouchableOpacity onPress={toggleVideo}>
 <Icon name={isVideoEnabled ? 'videocam' : 'videocam-off'} />
 </TouchableOpacity>
 <TouchableOpacity onPress={endCall}>
 <Icon name="call-end" />
 </TouchableOpacity>
 </View>
 </View>
 );
};

The RTCView component renders video streams from WebRTC. The mirror prop on the local video preview prevents the disorienting effect of seeing a reversed image, which is more natural for users during video calls.

Advanced Features and Optimization

Screen Sharing Implementation

Screen sharing extends video calls with presentation capabilities, useful for meetings and collaborative applications:

const useScreenShare = (peerConnection) => {
 const [isScreenSharing, setIsScreenSharing] = useState(false);

 const startScreenShare = async () => {
 const stream = await mediaDevices.getDisplayMedia({ video: true });
 const videoTrack = stream.getVideoTracks()[0];
 const sender = peerConnection.current.getSenders().find(s => s.track && s.track.kind === 'video');
 if (sender) {
 await sender.replaceTrack(videoTrack);
 }
 setIsScreenSharing(true);
 };

 const stopScreenShare = async () => {
 setIsScreenSharing(false);
 };

 return { isScreenSharing, startScreenShare, stopScreenShare };
};

The screen share implementation replaces the camera video track with the screen capture track, allowing the remote party to see screen content while maintaining the call connection.

Data Channels for Real-Time Messaging

WebRTC data channels enable peer-to-peer messaging alongside audio and video, useful for chat, file transfer, or game state synchronization:

const setupDataChannel = (peerConnection) => {
 const channel = peerConnection.createDataChannel('messages', { ordered: true });
 channel.addEventListener('message', (event) => {
 const message = JSON.parse(event.data);
 setMessages(prev => [...prev, { ...message, isOwn: false }]);
 });
 return channel;
};

Data channels can be configured for ordered delivery (like chat messages) or unordered delivery (like real-time game updates), with optional reliability guarantees.

Adaptive Video Quality

Network conditions vary significantly during mobile calls, making adaptive quality essential for maintaining call quality:

const useAdaptiveQuality = (peerConnection) => {
 const [networkQuality, setNetworkQuality] = useState('good');

 const checkNetworkQuality = async () => {
 const stats = await peerConnection.current.getStats();
 let packetsLost = 0, jitter = 0;

 stats.forEach(report => {
 if (report.type === 'inbound-rtp' && report.mediaType === 'video') {
 packetsLost = report.packetsLost || 0;
 jitter = report.jitter || 0;
 }
 });

 if (packetsLost > 5 || jitter > 50) setNetworkQuality('poor');
 else if (packetsLost > 2 || jitter > 30) setNetworkQuality('fair');
 else setNetworkQuality('good');
 };

 return { networkQuality, checkNetworkQuality };
};

By monitoring ICE statistics including packet loss and jitter, applications can dynamically adjust video quality to match available bandwidth, ensuring a smooth call experience despite changing network conditions. Implementing these advanced features requires expertise in web development and real-time communication protocols.

Production Deployment Considerations

TURN Server Setup

While STUN servers handle basic NAT traversal, TURN servers relay traffic when direct peer-to-peer connection fails. Open-source TURN server options include Coturn, which provides both STUN and TURN functionality:

# Coturn configuration
listening-port=3478
tls-listening-port=5349
realm=your-realm.com
external-ip=YOUR_PUBLIC_IP

Production deployments should use time-limited credentials that expire after a set period, improving security by limiting the window for credential misuse.

Security Considerations

WebRTC applications should implement several security measures:

  • TURN authentication with time-limited, rotating credentials
  • Authentication and authorization for signaling endpoints
  • WSS (WebSocket Secure) for all signaling connections
  • Rate limiting on signaling endpoints
  • HTTPS/WSS for all communications

Performance Optimization

Proper cleanup prevents memory leaks:

const cleanup = () => {
 if (localStream) {
 localStream.getTracks().forEach(track => track.stop());
 }
 if (peerConnection.current) {
 peerConnection.current.close();
 }
};

React Native applications must explicitly stop media tracks and close peer connections when components unmount, as native resources won't be automatically garbage collected. Memory management is critical for applications that maintain long-running video calls.

Network Connectivity Handling

Mobile networks frequently change connectivity states, requiring graceful handling:

const useNetworkHandling = (peerConnection, reconnectFn) => {
 const [isOnline, setIsOnline] = useState(true);

 useEffect(() => {
 const unsubscribe = NetInfo.addEventListener(state => {
 setIsOnline(state.isConnected);
 if (state.isConnected && !isOnline) {
 handleNetworkRestore();
 } else if (!state.isConnected) {
 handleNetworkLoss();
 }
 });
 return unsubscribe;
 }, [isOnline]);

 return { isOnline };
};

Monitoring network state allows applications to pause video calls during connectivity loss and attempt reconnection when connectivity is restored. Building production-grade video calling requires careful attention to these reliability patterns.

For organizations looking to implement real-time communication features, our custom software development services can help architect and build scalable video communication solutions.

Frequently Asked Questions

What platforms does react-native-webrtc support?

react-native-webrtc supports iOS (arm64, x86_64), Android (armeabi-v7a, arm64-v8a, x86, x86_64), and tvOS (arm64). The package uses WebRTC M124 with Unified Plan SDP format.

Do I need a TURN server for production?

Yes, production applications should include TURN server infrastructure. STUN servers handle basic NAT traversal, but TURN servers relay traffic when direct peer-to-peer fails due to symmetric NATs or restrictive firewalls.

How does WebRTC handle network changes?

WebRTC uses ICE (Interactive Connectivity Establishment) to discover network paths. When connectivity changes, the application can monitor connection state and trigger reconnection attempts. Adaptive bitrate adjusts video quality based on available bandwidth.

What is the signaling server role?

The signaling server coordinates the initial connection by exchanging session descriptions (offers/answers) and ICE candidates between peers. Once connected, media flows directly peer-to-peer without involving the signaling server.

How do I implement screen sharing?

Use mediaDevices.getDisplayMedia() to capture the screen, then replace the video track in the peer connection with the screen track using sender.replaceTrack(). Remember to handle the 'ended' event when users stop screen sharing.

Conclusion

Building video calling functionality in React Native with react-native-webrtc requires understanding WebRTC fundamentals, proper platform configuration, and careful state management. The react-native-webrtc package provides a bridge to native WebRTC implementations, enabling cross-platform video communication with reasonable performance characteristics.

Key implementation considerations include properly configuring permissions for both iOS and Android, implementing a signaling server to coordinate peer connections, and handling the complex WebRTC connection flow including offer/answer exchange and ICE candidate negotiation. Production deployments require TURN server infrastructure for reliable connectivity across all network types.

Performance optimization through adaptive bitrate streaming, proper memory management, and network state handling ensures a smooth user experience across varying conditions. Error handling for common failure modes like permission denial, hardware issues, and network disruption improves reliability.

The react-native-webrtc ecosystem continues to evolve with the underlying WebRTC specification, providing access to modern features like simulcast and improved codec support. Developers should stay current with the official repository documentation and community best practices for optimal results.

If you're planning to integrate video calling into your mobile application, consider partnering with experienced developers who understand both the technical complexity and user experience requirements. Our team specializes in mobile app development and can help you build robust real-time communication features that delight your users.

Ready to Build Your Video Calling App?

Our team specializes in React Native development and real-time communication features. Contact us to discuss your project requirements.

Sources

  1. Viewlytics - React Native WebRTC Complete Guide - Comprehensive 2025 guide covering setup, implementation, optimization, and advanced features with practical code examples
  2. react-native-webrtc GitHub Repository - Official repository with installation, configuration, and API documentation