React Native Audio Room Tutorial

This tutorial aims to guide you through the process of creating an audio room experience reminiscent of platforms such as Twitter Spaces or Clubhouse. The outcome, as depicted in the image below, will encompass the following functionalities:

  • Backstage Mode: Initiate the call with your co-hosts, allowing for pre-live discussions.
  • Global Edge Network: Calls operate on Stream's worldwide edge network, ensuring low latency and scalability.
  • No Listener Limit: Rooms can accommodate an unlimited number of listeners.
  • Raised Hand Feature: Listeners can indicate their desire to speak by raising their hand and subsequently being invited by the host.
  • Enhanced Audio Reliability: Audio tracks are transmitted multiple times to enhance overall reliability.

Preview of the final result

Step 1 - Setup a new React Native app

Create a new React Native app using the official template,

Step 2 - Install the SDK and declare permissions

In order to install the Stream Video React Native SDK, run the following command in your terminal of choice:

The SDK requires installing some peer dependencies. You can run the following command to install them:

Declare permissions

  1. Android
  2. iOS

In AndroidManifest.xml add the following permissions before the application section.

If you plan to also support Bluetooth devices then also add the following.

Android Specific installation

In android/build.gradle add the following inside the buildscript section:

In android/app/build.gradle add the following inside the android section:

In android/gradle.properties add the following:

Run the app

To ensure the best possible experience, we highly recommend running the app on a physical device. This is due to the limitations in audio and video device support on emulators. You can refer to the React Native documentation for guidance on running the app on a physical device.

However, if you still prefer to use an emulator, execute the following command:

Step 3 - Understand the basics

Before we dive deep into writing code, there are two concepts you should be familiar with - StreamVideoClient and Call.

StreamVideoClient is the low level JavaScript client used by the SDK to communicate with the Stream service. It provides all the necessary methods to connect user Stream service, query calls, create call etc. And a call refers to an instance of the Call class and is utilized for performing call-specific actions, such as joining a call, muting participants, leaving a call, and more.

In this example

  • apiKey is the API key of your Stream Video application available on dashboard
  • user is the user object { id: "john_smith", "name": "John Smith" }
  • and token is the user token generated by your server-side API. For development purpose, you can use the token generated by the Token Generator. You can read more information about client authentication on the Client & Authentication guide.

A call of type audio_room can exist in either of two states: backstage and live.

  • During the backstage mode, only the host and designated speakers have permission to join the call.
  • In the live mode, anyone can join the call.

From the dashboard you can disable backstage mode or fine tune the permissions for backstage mode.

Client instance and call instance are made accessible to all the video components from SDK via StreamVideo component and StreamCall component respectively. In your app you need to wrap your component tree with StreamVideo component and provide the client instance to it as a prop. client creation would normally take place during sign-in stage of the application. And similarly you need to wrap your call specific UI components with StreamCall component and provide the call instance to it as a prop.

StreamVideo and StreamCall components are basically context providers, and they enable you to consume hooks provided by the SDK. These hooks do all the heavy lifting around call management and provide you with the necessary state and methods to build your UI.

Step 4 - Setup Starter UI

Lets begin by creating a basic UI for our audio room. Normally you would use a navigation library like React Navigation to navigate between screens. But for this tutorial we'll keep it simple and mock the navigation using a state variable - activeScreen.

Within your tutorial app, create a folder named src and create the following files within it:

  • src/HomeScreen.tsx
  • src/CallScreen.tsx
  • src/AudioRoomUI.tsx

Now copy the following content into the respective files (as mentioned in header):

  1. App.tsx
  2. src/AudioRoomUI.tsx
  3. src/CallScreen.tsx
  4. src/HomeScreen.tsx

In App.tsx we have hardcoded the placeholders of apiKey, userId, token and callId for simplicity of tutorial. To actually run this sample we need a valid user token. The user token is typically generated by your server side API. When a user logs in to your app you return the user token that gives them access to the call. To make this tutorial easier to follow we'll generate a user token for you:

Please update REPLACE_WITH_API_KEY, REPLACE_WITH_USER_ID, REPLACE_WITH_TOKEN and REPLACE_WITH_CALL_ID with the actual values shown below:

In actual application you will want to store apiKey in a secure storage or in environment file.

Here are credentials to try out the app with:

PropertyValue
API KeyWaiting for an API key ...
Token Token is generated ...
User IDLoading ...
Call IDCreating random call ID ...

Hit save and you should see on your emulator or device the following UI.

Preview of the Home ScreenPreview of empty audio room
Preview of the Home Screen Preview of empty audio room

Step 5 - Setup Video Client

Within this configuration, we will establish a StreamVideoClient instance and facilitate the user's connection to the Stream service. In real application, client creation should be encapsulated within a useEffect hook and during unmount you should call client.disconnectUser() to avoid creating multiple websockets.

Client instance needs to be provided to StreamVideo component and it will provide the client instance to all the child components using React Context. It needs to go at the top of the component tree.

You wouldn't see any change in the UI at this point, since we haven't joined the call yet.

Step 6 - Create & Join a call​

In this step we will create and join a call. Call will be stored in a state variable call and it needs to be provided to StreamCall component. As explained earlier, StreamCall component is provided by the SDK and it provides all the necessary hooks for configuring UI around audio room. We will explore these hooks later in the tutorial.

Open up src/CallScreen.tsx and replace it with this code:

Also as explained earlier in Understand the Basics section, call can be created or accessed using client.call(...) method. Thus we need access to client inside CallScreen component. We will use the useStreamVideoContext hook to get access to the client instance.

We will put the joining logic inside useEffect hook, so we automatically join the call when user goes to CallScreen.

Please note the difference between call.join and call.goLive methods. When a call is created, it is in backstage mode by default, where only the host and speakers are allowed to join. User needs to have permission to join the backstage mode, which we will explore later in the tutorial. Normal user or audience of the audio room will be able to join the call when host calls call.goLive(). This makes it easy to try out your room and talk to your co-hosts before going live. You can enable or disable backstage mode in the dashboard.

For now we will call call.goLive() immediately after joining the call, so that we can test the audio room.

To enhance the interactivity of this tutorial moving forward, kindly follow these steps:

  • Give the app a refresh, then tap the "Join Audio Room" button within your mobile tutorial app.
  • Access the web version of the audio room on your browser by clicking the "Join Call" link provided below, and subsequently, hit the "Join" button.
For testing you can join the call on our web-app: Join Call

Currently, you won't have the ability to speak, as we haven't set up the user interface for the audio room. However, you will be able to view the current user listed among the participants on the web application.

Step 7 - Configure UI for Audio Room

Up until now, we've effectively established and entered the call. Moving forward, our focus will be on setting up the audio room's user interface. This involves presenting a participant list, indicating their speaking status, offering controls to enable or disable both live mode and audio transmission, and more.

As previously mentioned, all essential call information can be accessed through the SDK's provided hooks, facilitated by the StreamCall component. You can conveniently access a range of hooks related to the call's state by utilizing the useCallStateHooks hook as demonstrated below. By utilizing these hooks, you can confidently depend on the most up-to-date state information.

For a comprehensive list of all the available hooks, kindly consult the Call and Participant State guide.

Now, we'll establish a basic structure for the UI by crafting the subsequent components within the AudioRoomUI component:

  • AudioRoomDescription: This component will display the call's title and participant count.
  • AudioRoomParticipants: Here, you'll showcase the list of participants along with their speaking status.
  • AudioRoomControlsPanel: This component will house controls to activate or deactivate both live mode and audio transmission.

So please go ahead and create the following files:

  1. AudioRoomDescription
  2. AudioRoomParticipants
  3. AudioRoomControlsPanel

And lets add these components to AudioRoomUI:

If you refresh the app, you should see following UI:

Preview of the audio room UI

Audio Room Description

We will use the useCallCustomData to access custom data which we set as data.custom property in client.call(..) method which creating the call. And participant count can be accessed using useParticipants hook.

Upon refreshing the application, you will notice the title and participant count displayed in the UI. You can also experiment by joining and leaving the audio room using the web app, observing how the participant count dynamically changes on the mobile interface.

For testing you can join the call on our web-app: Join Call

Preview of the audio room UI

Audio Room Participants

We will use the useParticipants hook to access list of participants and their speaking status.

:::info In practical applications, depending on business requirements you may want to dynamically sort the participants list based on certain criteria for example dominant speaker first or alphabetical etc. You can pass comparator function to useParticipants hook to sort the participants list.

Please take a look at Participants Sorting guide for the details and various sorting options. :::

Typically, audio room applications incorporate an indicator to denote whether a participant is currently speaking. We can achieve this effect by utilizing the isSpeaking boolean property associated with each participant. This property can be used to dynamically apply a border around the user avatar.

If you refresh the app, you should see the participant list as following

Preview of the audio room UI

Audio Room Controls Panel

Let's expand the ControlPanel and add a button that controls the backstage of the room. In Create and Join Call step, we made the call live right after creating/joining it. We will move that logic to a toggle button where host or speakers can toggle live mode of the call.

Please create a file named ToggleLiveButton.tsx in src directory and add the following code. Also make the following changes to src/AudioRoomControlsPanel.tsx and src/CallScreen.tsx file.

  1. ToggleLiveButton
  2. AudioRoomControlsPanel
  3. CallScreen

Next we will add a button to mute or unmute the microphone button. Handling audio and video devices in a your application means working with MediaStream, MediaDeviceInfo and other WebRTC API objects. To make this simpler, we hide all the complexity inside the SDK and export utility functions and states. You can toggle the state of microphone using call.microphone.toggle() function and you can access the state of microphone using useMicrophoneState hook. Please check Camera and Microphone guide for more details.

Please create a file named ToggleMicButton.tsx in src directory as following and add it to AudioRoomControlsPanel component.

  1. ToggleMicButton
  2. AudioRoomControlsPanel

:::info However, if you wish for the app to seamlessly switch between the earphone and speaker, you can use the react-native-incall-manager. Please follow the usage docs of the library for this purpose and our Speaker management docs.

By default, the audio source is set to the device's speaker if the mode is video and device's earpiece if the mode is audio.

:::

If you refresh the app, you should be able to toggle live mode and mute/unmute the microphone.

Preview of the audio room UI

Step 8 - Requesting permission to speak

In real audio room applications, a participant may need to request permission to speak from the host or speakers. And host or speakers can grant or deny the request using call.grantPermissions or call.revokePermissions methods. They can subscribe to an event call.permission_requested to get notified when a participant requests permission to speak.

Let's first have a quick look at how the SDK call object exposes this:

  1. Request Permission
  2. Grant or Reject Permission

Now, let's begin by first developing the logic to initiate a permission request for speaking when the user taps on the microphone button.

Let's proceed to add an event listener for the event call.permissions_updated. This will keep the current user informed about any changes in permissions, whether they are granted or rejected by hosts or speakers. It's worth noting that the current user in the app will already possess permission to speak, as the default permission configuration allows the call creator to transmit audio.

To handle the things host side, let's add another component that shows the last incoming request as well as the buttons to grant / reject it

Please create a file PermissionsRequestsPanel.tsx inside src directory and make the following changes. The code is pretty much self explanatory.

And now we will add this component to AudioRoomUI component.

Now, for the purpose of testing:

  • Navigate to the web app.
  • Join the audio room.
  • Click on the raised hand (✋) button located in the top-right corner to request permission to speak.
  • You should observe the request being displayed in the mobile app as illustrated below:
For testing you can join the call on our web-app: Join Call

Preview of the final result

Step 9 - Leave the call

User can leave the call by calling call.leave() method. Lets add this functionality to "Leave Audio Room" button.

Other built-in features

There are a few more exciting features that you can use to build audio rooms

  • Query Calls:: You can query calls to easily show upcoming calls, calls that recently finished as well as call previews.
  • Reactions & Custom events: Reactions and custom events are supported.
  • Chat: Stream's chat SDKs are fully featured and you can integrate them in the call
  • Moderation: Moderation capabilities are built-in to the product
  • Transcriptions: Transcriptions aren't available yet, but they are due to launch soon

Recap

It was fun to see just how quickly you can build an audio-room for your app. Please do let us know if you ran into any issues. Our team is also happy to review your UI designs and offer recommendations on how to achieve it with Stream.

To recap what we've learned:

  • You set up a call with const call = client.call('audio_room', '123')
  • The call type audio_room controls which features are enabled and how permissions are set up
  • The audio_room by default enables backstage mode, and only allows admins and the creator of the call to join before the call goes live
  • When you join a call, real-time communication is set up for audio & video calling: await call.join()
  • Call state call.state and helper state access hooks make it easy to build your own UI
  • Calls run on Stream's global edge network of video servers. Being closer to your users improves the latency and reliability of calls. For audio rooms we use Opus RED and Opus DTX for optimal audio quality.

The SDKs enable you to build audio rooms, video calling and live-streaming in days.

We hope you've enjoyed this tutorial and please do feel free to reach out if you have any suggestions or questions.

Final Thoughts

In this video app tutorial we built a fully functioning React Native messaging app with our React Native SDK component library. We also showed how easy it is to customize the behavior and the style of the React Native video app components with minimal code changes.

Both the video SDK for React Native and the API have plenty more features available to support more advanced use-cases.

Give us Feedback!

Did you find this tutorial helpful in getting you up and running with React Native for adding video to your project? Either good or bad, we’re looking for your honest feedback so we can improve.

Next Steps

Create your free Stream account to start building with our Video & Audio SDKs, or contact our team if you have additional questions.

Chat Messaging

Build any kind of chat messaging experience without scalability or reliability issues.

Learn more about $ Chat Messaging

Enterprise

Available 99.999% uptime SLAs and industry-leading security to power the world's largest apps.

Learn more about $ Enterprise