Skip to main content
Version: 0.29.0

React Native Publishing

This tutorial explains how to publish a live stream from a React Native mobile app using Media over QUIC (MoQ) with Fishjam. To watch a stream instead, see React Native Subscribing.

It uses react-native-moq — React Native bindings for MoQKit, with a small, reactive hooks-based API. For the web equivalent, see Web Publishing.

info

If you're new to MoQ, then we recommend getting familiar with the MoQ with Fishjam explanation.

Requirements​

react-native-moq targets the React Native New Architecture (Fabric / TurboModules):

  • iOS 16+
  • Android API 30+

Installation​

npm install react-native-moq

Then install the iOS pods:

cd ios && pod install

Publishing captures the camera and microphone, so the host app is responsible for runtime permissions: request CAMERA / RECORD_AUDIO on Android, and add NSCameraUsageDescription / NSMicrophoneUsageDescription to Info.plist on iOS. The library does not request these for you.

tip

MoQ is a protocol with a well-defined negotiation, so a publisher and a subscriber don't need to use the same client library. A React Native app published with react-native-moq can be watched in the browser with @moq/watch, and vice versa.

Quickstart with the Sandbox API​

If you don't have a backend server set up, you can prototype publishing using the Sandbox API.

Obtaining a publisher connection URL​

For more on what the Sandbox API is and its limitations, see What is the Sandbox API?.

info

To obtain a MoQ connection URL you'll need your Sandbox API URL. If you don't have it already, see Sandbox API URL.

The useSandbox hook from @fishjam-cloud/react-native-client wraps the Sandbox API request for you:

import { useSandbox } from "@fishjam-cloud/react-native-client"; const PUBLISHER_PATH = "stream-alice"; const SANDBOX_API_URL = "YOUR_SANDBOX_API_URL"; // Inside a React component: const { getSandboxMoqPublisherAccess } = useSandbox({ sandboxApiUrl: SANDBOX_API_URL, }); // Request a publisher connection URL scoped to the publisher path const { connection_url: publishUrl } = await getSandboxMoqPublisherAccess(PUBLISHER_PATH);

Connecting and publishing​

react-native-moq is hooks-based. You open a session against the relay, capture the camera and microphone, and hand them to a publisher.

Open the session with the publisher connection URL, capture the camera and microphone, and publish the tracks:

import { useEffect } from "react"; import { Button, PermissionsAndroid, Platform } from "react-native"; import { PublisherView, useCamera, useMicrophone, usePublisher, useSession, } from "react-native-moq"; const PUBLISHER_PATH = "stream-alice"; const publishUrl = ""; // from the step above function PublishScreen() { // Open the MoQ session against the Fishjam relay using the publisher connection URL const session = useSession(publishUrl, (s) => s.connect()); const camera = useCamera({ position: "front" }); const microphone = useMicrophone(); const publisher = usePublisher(session); // Request camera + mic permissions on Android (iOS handles it automatically when Info.plist is configured) useEffect(() => { if (Platform.OS === "android") { PermissionsAndroid.requestMultiple([ PermissionsAndroid.PERMISSIONS.CAMERA, PermissionsAndroid.PERMISSIONS.RECORD_AUDIO, ]); } }, []); const isPublishing = publisher.state === "publishing"; return ( <> <PublisherView camera={camera} style={{ aspectRatio: 9 / 16 }} /> <Button title="Flip" onPress={camera.flip} /> <Button title={isPublishing ? "Stop" : "Publish"} disabled={session.state !== "connected" && !isPublishing} onPress={() => { if (isPublishing) publisher.stop(); else publisher.publish({ path: PUBLISHER_PATH, tracks: [camera, microphone], }); }} /> </> ); }

Once publisher.state === "publishing", the stream is live on the MoQ relay! Viewers can start watching by following React Native Subscribing.

info

Why a separate useSession and publish()?
The session owns the connection to the relay; the publisher reuses it. Because publishing rides on top of an open session, the same connection can subscribe and publish at once — pair usePublisher(session) with useBroadcasts(session, prefix) to do both.

Production with Server SDKs​

The Quickstart gets you publishing quickly. In production, your backend generates tokens with proper authorization, so you control who can publish.

A publisher connection URL grants write access to a specific path. Generate one on your backend and deliver it to the broadcasting client:

import { FishjamClient } from '@fishjam-cloud/js-server-sdk'; const fishjamClient = new FishjamClient({ fishjamId, managementToken, }); const streamPath = 'stream-alice'; // Generate a connection URL that allows publishing to 'stream-alice' const { connection_url: publishUrl } = await fishjamClient.createMoqAccess({ publishPath: streamPath, });

Deliver this connection URL to the mobile client, then use it to connect as described in Connecting and publishing.

See also​