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
- Expo
- Bare workflow
- 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
Install Expo dependencies
Follow instructions from official Expo documentation.
Install Fishjam
npx expo install @fishjam-cloud/react-native-client
Configure app permissions
Your app needs to have permissions configured in order to use the microphone and camera.
Android
Permissions below are required to stream audio and video with Fishjam on Android.
android.permission.CAMERAandroid.permission.RECORD_AUDIOandroid.permission.MODIFY_AUDIO_SETTINGS
- 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" ] } } }
Add required permissions to the AndroidManifest.xml file.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> ... <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTING"/> ... </manifest>
iOS
- Expo
- Bare workflow
You don't have to make any changes to run app on iOS.
To update default content of permission alert, you can add these settings 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
npx expo prebuild
Get your Fishjam ID
- Log in to Fishjam Dashboard
- Navigate to your Sandbox environment
- Copy your Fishjam ID
Quick example
To quickly test our streaming infrastructure use FishjamRoom component. It encapsulates all the steps needed to start streaming.
Example usage:
import { FishjamRoom, useSandbox } from "@fishjam-cloud/react-native-client"; import React, { useEffect, useState } from "react"; export default function HomeScreen() { const fishjamId = "YOUR_FISHJAM_ID"; const [peerToken, setPeerToken] = useState<string | null>(null); const { getSandboxPeerToken } = useSandbox({ fishjamId: fishjamId }); useEffect(() => { const fetchPeerToken = async () => { try { const peerToken = await getSandboxPeerToken( "example-room-name", "example-peer-name", ); setPeerToken(peerToken); } catch (e) { console.error("Error connecting to Fishjam", e); } }; fetchPeerToken(); }, []); if (!peerToken) { return null; } return <FishjamRoom fishjamId={fishjamId} peerToken={peerToken} />; }
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:
import React, { useCallback } from "react"; import { Button } from "react-native"; import { useCamera, useConnection, useSandbox, } from "@fishjam-cloud/react-native-client"; // Check https://fishjam.io/app for your Fishjam ID const FISHJAM_ID = "YOUR_FISHJAM_ID"; export function StartStreamingButton({ roomName, peerName, }: { roomName: string; peerName: string; }) { const { prepareCamera } = useCamera(); const { joinRoom } = useConnection(); const { getSandboxPeerToken } = useSandbox({ fishjamId: FISHJAM_ID }); const startStreaming = useCallback(async () => { // 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); // Prepare camera await prepareCamera({ cameraEnabled: true }); // Join the room await joinRoom({ peerToken, fishjamId: FISHJAM_ID }); }, [joinRoom, prepareCamera, roomName, peerName]); return <Button title="Start Streaming" onPress={startStreaming} />; }
Step 3: Check connection status
Monitor your connection status:
import React from "react"; import { Text } from "react-native"; import { useConnection } from "@fishjam-cloud/react-native-client"; // ---cut--- export function ConnectionStatus() { const { peerStatus } = useConnection(); return <Text>Status: {peerStatus}</Text>; }
Step 4: Display your video
Show your own video stream:
import React from "react"; import { VideoPreviewView } from "@fishjam-cloud/react-native-client"; export function StreamPreview() { return <VideoPreviewView style={{ height: 300, width: 300 }} />; }
Step 5: Display other participants
Show video from other peers in the room:
import React from "react"; import { usePeers, VideoRendererView, } from "@fishjam-cloud/react-native-client"; import { View } from "react-native"; // ---cut--- export function ParticipantsView() { const { remotePeers } = usePeers(); const videoTracks = remotePeers.flatMap((peer) => peer.tracks.filter((track) => track.type === "Video" && track.isActive), ); return ( <View> {videoTracks.map((track) => ( <VideoRendererView key={track.id} trackId={track.id} videoLayout="FIT" style={{ height: 300, width: 300 }} /> ))} </View> ); }
Complete example
Here's a complete working example:
import { Text, View, Button } from "react-native"; import { usePeers, useCamera, useConnection, useSandbox, VideoRendererView, VideoPreviewView, } from "@fishjam-cloud/react-native-client"; import React, { useCallback } from "react"; // Check https://fishjam.io/app for your Fishjam ID const FISHJAM_ID = `${process.env.EXPO_PUBLIC_FISHJAM_ID}`; function StartStreamingButton({ roomName, peerName, }: { roomName: string; peerName: string; }) { const { prepareCamera } = useCamera(); const { joinRoom } = useConnection(); const { getSandboxPeerToken } = useSandbox({ fishjamId: FISHJAM_ID }); const startStreaming = useCallback(async () => { const peerToken = await getSandboxPeerToken(roomName, peerName); await prepareCamera({ cameraEnabled: true }); await joinRoom({ peerToken, fishjamId: FISHJAM_ID }); }, [joinRoom, prepareCamera, roomName, peerName]); return <Button title="Start Streaming" onPress={startStreaming} />; } function ConnectionStatus() { const { peerStatus } = useConnection(); return <Text>Status: {peerStatus}</Text>; } function StreamPreview() { return <VideoPreviewView style={{ height: 300, width: 300 }} />; } function ParticipantsView() { const { remotePeers } = usePeers(); const videoTracks = remotePeers.flatMap((peer) => peer.tracks.filter((track) => track.type === "Video" && track.isActive), ); return ( <View> {videoTracks.map((track) => ( <VideoRendererView key={track.id} trackId={track.id} videoLayout="FIT" style={{ height: 300, width: 300 }} /> ))} </View> ); } export default function Index() { return ( <View style={{ flex: 1, alignItems: "center", }} > <StartStreamingButton roomName="test-room" peerName="test-peer" /> <ConnectionStatus /> <Text>Your Stream:</Text> <StreamPreview /> <Text>Other Participants:</Text> <ParticipantsView /> </View> ); }
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: