Skip to main content
Version: Next

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