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 and Sandbox API URL
- Log in to Fishjam Dashboard
- Copy your Fishjam ID
- Navigate to the Sandbox tab
- Copy your Sandbox API URL from the Sandbox API section
The Fishjam ID is required by FishjamProvider to connect to the media server. The Sandbox API URL is used by useSandbox to request test peer tokens from the sandbox.
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"; constSANDBOX_API_URL = "YOUR_SANDBOX_API_URL"; export functionJoinRoomButton () { const {joinRoom } =useConnection (); const {initializeDevices } =useInitializeDevices (); const {getSandboxPeerToken } =useSandbox ({sandboxApiUrl :SANDBOX_API_URL , }); consthandleJoinRoom = async () => { constroomName = "testRoom"; constpeerName = "testUser"; // For testing with the Sandbox API, use getSandboxPeerToken // For production apps, get the peerToken from your own backend instead 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:
importReact from "react"; import {View ,Text } from "react-native"; import {usePeers ,RTCView , typeMediaStream , } from "@fishjam-cloud/react-native-client"; functionVideoPlayer ({stream }: {stream :MediaStream | null | undefined }) { return ( <View > {stream ? ( <RTCView mediaStream ={stream }objectFit ="cover"style ={{height : 300,width : 300 }} /> ) : ( <Text >No video</Text > )} </View > ); } export functionParticipantsView () { 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:
functionVideoPlayer ({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" /> ); } functionVideoCall () { const {joinRoom ,peerStatus } =useConnection (); const {cameraStream } =useCamera (); const {remotePeers } =usePeers (); const {initializeDevices } =useInitializeDevices (); const {getSandboxPeerToken } =useSandbox ({sandboxApiUrl :SANDBOX_API_URL , }); const [isJoined ,setIsJoined ] =useState (false); consthandleJoin = async () => { constroomName = "testRoom"; constpeerName = `user_${Date .now ()}`; // Initialize devices first awaitinitializeDevices (); // For testing with the Sandbox API, use getSandboxPeerToken // For production apps, get the peerToken from your own backend instead constpeerToken = awaitgetSandboxPeerToken (roomName ,peerName ); awaitjoinRoom ({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 > ); } conststyles =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 functionApp () { 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
- Explore React Native examples
Or learn more about Fishjam concepts: