React Native Quick Start
This tutorial will guide you through integrating Fishjam into your React Native application step by step.
By the end, you'll have a working video streaming app and understand the core concepts.
What you'll build
A simple React Native app that can join conference calls and stream audio/video between participants.
What you'll learn
- How to install and configure Fishjam SDK
- How to join a room and start streaming
- How to display video and play audio from other participants
- How to check connection status
Prerequisites
- React Native development environment set up
- Access to Fishjam Dashboard
Step 1: Install and configure
Install the package
- npm
- Yarn
- pnpm
- Bun
npm install @fishjam-cloud/react-native-client
yarn add @fishjam-cloud/react-native-client
pnpm add @fishjam-cloud/react-native-client
bun add @fishjam-cloud/react-native-client
Configure app permissions
Your app needs to have permissions configured in order to use the microphone and camera.
Android
- Expo
- Bare workflow
Add required permissions to the app.json file:
{ "expo": { "android": { "permissions": [ "android.permission.CAMERA", "android.permission.RECORD_AUDIO", "android.permission.MODIFY_AUDIO_SETTINGS", "android.permission.ACCESS_NETWORK_STATE" ] } } }
Add required permissions to 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.MODIFY_AUDIO_SETTING"/>
iOS
- Expo
- Bare workflow
Add camera and microphone usage descriptions to app.json:
{ "expo": { "ios": { "infoPlist": { "NSCameraUsageDescription": "Allow $(PRODUCT_NAME) to access your camera.", "NSMicrophoneUsageDescription": "Allow $(PRODUCT_NAME) to access your microphone." } } } }
Ensure Info.plist contains camera and microphone usage description entries:
<key>NSCameraUsageDescription</key> <string>Allow $(PRODUCT_NAME) to access your camera.</string> <key>NSMicrophoneUsageDescription</key> <string>Allow $(PRODUCT_NAME) to access your microphone.</string>
Build native dependencies
- Expo
- Bare workflow
npx expo prebuild
cd ios && pod install
Get your Fishjam ID
- Log in to Fishjam Dashboard
- Navigate to your Sandbox environment
- Copy your Fishjam ID
Set up Fishjam context
Wrap your app in the FishjamProvider component:
// Check https://fishjam.io/app/ for your Fishjam ID constFISHJAM_ID = "YOUR_FISHJAM_ID"; export default functionRoot () { return ( <FishjamProvider fishjamId ={FISHJAM_ID }> <App /> </FishjamProvider > ); }
This won't work on the iOS Simulator, as the Simulator can't access the camera. Test on a real device.
For more detailed implementation follow the steps below.
Step 2: Join a room and start streaming
Create a component that joins a room and starts streaming video:
importReact from "react"; import {Button } from "react-native"; import {useConnection ,useSandbox ,useInitializeDevices , } from "@fishjam-cloud/react-native-client"; export functionJoinRoomButton () { const {joinRoom } =useConnection (); const {initializeDevices } =useInitializeDevices (); const {getSandboxPeerToken } =useSandbox (); consthandleJoinRoom = async () => { constroomName = "testRoom"; constpeerName = "testUser"; // In sandbox environment, you can get the peer token from our sandbox API // In production environment, you need to get it from your backend constpeerToken = awaitgetSandboxPeerToken (roomName ,peerName ); // Start camera by selecting the first available camera awaitinitializeDevices ({enableAudio : false }); // or just initializeDevices(); if you want both camera and mic // Join the room awaitjoinRoom ({peerToken }); }; return <Button title ="Join Room"onPress ={handleJoinRoom } />; }
Step 3: Check connection status
Monitor your connection status:
export functionConnectionStatus () { const {peerStatus } =useConnection (); return <Text >Status: {peerStatus }</Text >; }
Step 4: Display your video
Show your own video stream:
importReact from "react"; import {Text ,View } from "react-native"; import {RTCView ,useCamera } from "@fishjam-cloud/react-native-client"; export functionStreamPreview () { const {cameraStream } =useCamera (); return ( <View > {cameraStream ? ( <RTCView mediaStream ={cameraStream }style ={{height : 300,width : 300 }}objectFit ="cover"mirror ={true} /> ) : ( <Text >No camera stream</Text > )} </View > ); }
Step 5: Display other participants
Show video from other peers in the room:
import React from "react"; import { View, Text } from "react-native"; import { usePeers, RTCView } from "@fishjam-cloud/react-native-client"; function VideoPlayer({ stream }: { stream: MediaStream | null | undefined }) { return ( <View> {stream ? ( <RTCView mediaStream={stream} objectFit="cover" style={{ height: 300, width: 300 }} /> ) : ( <Text>No video</Text> )} </View> ); } export function ParticipantsView() { const { remotePeers } = usePeers(); return ( <View> {remotePeers.map((peer) => ( <View key={peer.id}> {peer.cameraTrack?.stream && ( <VideoPlayer stream={peer.cameraTrack.stream} /> )} </View> ))} </View> ); }
Complete example
Here's a complete working app:
import React, { useState } from "react"; import { View, Text, Button, ScrollView, StyleSheet } from "react-native"; import { FishjamProvider, useConnection, useCamera, usePeers, useInitializeDevices, useSandbox, RTCView, } from "@fishjam-cloud/react-native-client"; // Check https://fishjam.io/app/ for your Fishjam ID const FISHJAM_ID = "YOUR_FISHJAM_ID"; // ---cut--- function VideoPlayer({ stream }: { stream: MediaStream | null | undefined }) { if (!stream) { return ( <View style={styles.videoPlaceholder}> <Text>No video</Text> </View> ); } return ( <RTCView mediaStream={stream} style={styles.video} objectFit="cover" /> ); } function VideoCall() { const { joinRoom, peerStatus } = useConnection(); const { cameraStream } = useCamera(); const { remotePeers } = usePeers(); const { initializeDevices } = useInitializeDevices(); const { getSandboxPeerToken } = useSandbox(); const [isJoined, setIsJoined] = useState(false); const handleJoin = async () => { const roomName = "testRoom"; const peerName = `user_${Date.now()}`; // Initialize devices first await initializeDevices(); // In sandbox environment, you can get the peer token from our sandbox API // In production environment, you need to get it from your backend const peerToken = await getSandboxPeerToken(roomName, peerName); await joinRoom({ peerToken }); setIsJoined(true); }; return ( <ScrollView style={styles.container}> <Text style={styles.title}>Fishjam Video Call</Text> <Text style={styles.status}>Status: {peerStatus}</Text> {!isJoined && <Button title="Join Room" onPress={handleJoin} />} {cameraStream && ( <View style={styles.section}> <Text style={styles.sectionTitle}>Your Video</Text> <VideoPlayer stream={cameraStream} /> </View> )} <View style={styles.section}> <Text style={styles.sectionTitle}>Other Participants</Text> {remotePeers.length === 0 ? ( <Text>No other participants</Text> ) : ( remotePeers.map((peer) => ( <View key={peer.id} style={styles.participant}> {peer.cameraTrack?.stream && ( <VideoPlayer stream={peer.cameraTrack.stream} /> )} </View> )) )} </View> </ScrollView> ); } const styles = StyleSheet.create({ container: { flex: 1, padding: 20, }, title: { fontSize: 24, fontWeight: "bold", marginBottom: 10, }, status: { fontSize: 16, marginBottom: 20, }, section: { marginTop: 20, }, sectionTitle: { fontSize: 18, fontWeight: "600", marginBottom: 10, }, participant: { marginBottom: 10, }, video: { height: 200, width: "100%", borderRadius: 8, }, videoPlaceholder: { height: 200, width: "100%", backgroundColor: "#000", borderRadius: 8, justifyContent: "center", alignItems: "center", }, }); export default function App() { return ( <FishjamProvider fishjamId={FISHJAM_ID}> <VideoCall /> </FishjamProvider> ); }
Next steps
Now that you have a basic app working, explore these how-to guides:
- How to handle screen sharing
- How to implement background streaming
- How to handle reconnections
- How to work with metadata
Or learn more about Fishjam concepts: