How to play sounds in React Native using react-native-sound

From the perspective of the end-user, sounds in an app hold their own importance for a better and refined experience. A little ding for a new notification, a “whoosh” sound for an email sent, or a crumbling noise for deleting files holds a very important place in building a better user experience for your app.

In this guide, we’ll explore react-native-sound, a powerful audio component that can handle sound-related tasks for your React Native app.

To follow along, you should be familiar with the basics of React Native, including JSX, components (class and functional), and styling.

You could simply copy and paste the code blocks from this guide, but I would suggest reading through the whole tutorial for a complete understanding. This guide assumes you’ve already done the basic setup for you app.

What is react-native-sound?

react-native-sound is a module in React Native for playing sound clips on iOS, Android, and Windows. This module enables you to add audio from various sources, such as the app bundle (native), the JavaScript bundle, or remote paths (local storage or remote URLs).

react-native-sound is more of a class than a class component, which helps to control the instances using predefined methods without updating states or props. This alleviates concerns related to slow rerenders of the app.

Though its documentation warns that react-native-sound is “alpha quality and may have bugs,” its one of the most popular and widely used React Native sound libraries today.

Setting up react-native-sound (iOS and Android)

To get started using react-native-sound, you’ll need the sounds you want to play in your app if they are not remotely available (i.e., they are inside the app’s package rather than in the device’s external storage or on a remote server).

To add react-native-sound to your app, simply enter the following command inside your app directory using your preferred package manager (e.g., npm or yarn):

$ yarn add react-native-sound

If you’re building for Android, linking is handled during the building process.

For iOS, simply navigate to the iOS directory and call pod install. This will add pods required for the react-native-sound.

Adding sounds to your React Native app

Now it’s time to add the sounds you want to play. This step is just for the sake of learning how to bundle the audio files inside the native package. We’ll also go over a simpler way of requiring the audio files from the assets directory.

For Android, create the raw directory and copy the sounds to that:

{appDirectory}/android/app/src/main/res/raw

For iOS, open the workspace in Xcode, then right click on the project and click A****dd files to {project_name}, as shown below:

Adding sounds to a React Native app

Selecting the audio files

The result should look something like this:

After adding the sounds

After adding the sound files, just build the app and run it on a simulator or, preferably, on a real device.

Playing a sound from a bundle in React Native

After successfully running the app, it’s time to start coding.

First we need to import the sound component in the app:

import Sound from 'react-native-sound';

Before, setting up the sound we’re going to play, we need to specify the category of sound:

Sound.setCategory('Playback');

To initialize the sound, use the following code:

var ding = new Sound('ding.mp3', Sound.MAIN_BUNDLE, (error) => {
  if (error) {
    console.log('failed to load the sound', error);
    return;
  }
  // when loaded successfully
  console.log('duration in seconds: ' + whoosh.getDuration() + 'number of channels: ' + whoosh.getNumberOfChannels());
});

The above code uses the first argument, ding.mp3, a file from the bundle specified using the second argument, Sound.MAIN_BUNDLE, and returns a callback in the third argument.

\``getDuration is used to get the duration of the audio file in seconds, and getNumberOfChannels is used to get the count of the audio channels.

To set the volume of the playback, we can use the following method:

ding.setVolume(1);

Here, 1 is the highest volume and 0 is the lowest with the difference between them in decimal places — e.g., to reduce the volume to 50 percent, you would set it to 0.5.

To play the file, we can use the play method from Sound, as follows:

ding.play(success => {
  if (success) {
    console.log('successfully finished playing');
  } else {
    console.log('playback failed due to audio decoding errors');
  }
});

There is a callback after successfully completing the payback (or if anything goes wrong, for that matter).

To sum up the whole thing:

  1. Firstly we need to set the category of the sound that is to be played.
  2. We need to initialise a sound file.
  3. Adjust the volume, if required.
  4. Play the sound.

Here’s the final code to play a sound from the bundle.

import React, {useEffect} from 'react';
import {View, StyleSheet, TouchableOpacity} from 'react-native';
import Ionicons from 'react-native-vector-icons/Ionicons';

var Sound = require('react-native-sound');


Sound.setCategory('Playback');


var ding = new Sound('ding.mp3', Sound.MAIN_BUNDLE, (error) => {
if (error) {
    console.log('failed to load the sound', error);
    return;
  }
  // if loaded successfully
  console.log('duration in seconds: ' + ding.getDuration() + 'number of channels: ' + ding.getNumberOfChannels());
 
});
const App = () => {
  useEffect(() => {
    ding.setVolume(1);
    return () => {
      ding.release();
    };
  }, []);
  const playPause = () => {
    ding.play(success => {
      if (success) {
        console.log('successfully finished playing');
      } else {
        console.log('playback failed due to audio decoding errors');
      }
    });
  };
  return (
    <View style={styles.container}>
      <TouchableOpacity style={styles.playBtn} onPress={playPause}>
        <Ionicons name={'ios-play-outline'} size={36} color={'#fff'} />
      </TouchableOpacity>
    </View>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#000',
  },
  playBtn: {
    padding: 20,
  },
});
export default App;

Importing a sound file in React Native

Now, as for the files from the assets directory (or any directory from the app for that matter), we can just use same old require or import to get the instance of the file and play it using react-native-sound.

Just import the file —

import dings from './src/assets/ding.mp3';

— and use it in the Sound package, as shown below:

var ding = new Sound(dings, error => {
  if (error) {
    console.log('failed to load the sound', error);
    return;
  }
  // if loaded successfully
  console.log('duration in seconds: ' + ding.getDuration() + 'number of channels: ' + ding.getNumberOfChannels());
});

The difference when using an imported or required file from the assets as opposed to bundled files is that you don’t have to specify the basePath for the file. Instead, the callback takes its place.

Here’s the code for the component to play an imported file:

import React, {useEffect} from 'react';
import {View, StyleSheet, TouchableOpacity} from 'react-native';
import dings from './src/assets/ding.mp3';
import Ionicons from 'react-native-vector-icons/Ionicons';

var Sound = require('react-native-sound');

Sound.setCategory('Playback');

var ding = new Sound(dings, error => {
  if (error) {
    console.log('failed to load the sound', error);
    return;
  }
  // if loaded successfully
  console.log(
    'duration in seconds: ' +
      ding.getDuration() +
      'number of channels: ' +
      ding.getNumberOfChannels(),
  );
});
const App = () => {
  useEffect(() => {
    ding.setVolume(1);
    return () => {
      ding.release();
    };
  }, []);
  const playPause = () => {
    ding.play(success => {
      if (success) {
        console.log('successfully finished playing');
      } else {
        console.log('playback failed due to audio decoding errors');
      }
    });
  };
  return (
    <View style={styles.container}>
      <TouchableOpacity style={styles.playBtn} onPress={playPause}>
        <Ionicons name={'ios-play-outline'} size={36} color={'#fff'} />
      </TouchableOpacity>
    </View>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#000',
  },
  playBtn: {
    padding: 20,
  },
});
export default App;

Below is the result of the above code. Just tap the play button to play the sound.

Playing sound files from remote paths

You may want to play remote files or files from local storage. This is basically as easy as playing a bundled file using react-native-sound. You just need to add the URL as the first parameter to Sound and set the second parameter as null because the file is from a remote or local storage, not from the app.

var audio = new Sound(
  'https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3',
  null,
  error => {
    if (error) {
      console.log('failed to load the sound', error);
      return;
    }
    // if loaded successfully
    console.log(
      'duration in seconds: ' +
        audio.getDuration() +
        'number of channels: ' +
        audio.getNumberOfChannels(),
    );
  },
);

Below is a complete example of how to play, pause, and release the audio after the app is closed or the component is unmounted:

import React, {useEffect, useState} from 'react';
import {View, StyleSheet, TouchableOpacity} from 'react-native';
import Ionicons from 'react-native-vector-icons/Ionicons';

var Sound = require('react-native-sound');


Sound.setCategory('Playback');

var audio = new Sound(
  'https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3',
  null,
  error => {
    if (error) {
      console.log('failed to load the sound', error);
      return;
    }
    // if loaded successfully
    console.log(
      'duration in seconds: ' +
        audio.getDuration() +
        'number of channels: ' +
        audio.getNumberOfChannels(),
    );
  },
);
const App = () => {
  const [playing, setPlaying] = useState();
  useEffect(() => {
    audio.setVolume(1);
    return () => {
      audio.release();
    };
  }, []);
  const playPause = () => {
    if (audio.isPlaying()) {
      audio.pause();
      setPlaying(false);
    } else {
      setPlaying(true);
      audio.play(success => {
        if (success) {
          setPlaying(false);
          console.log('successfully finished playing');
        } else {
          setPlaying(false);
          console.log('playback failed due to audio decoding errors');
        }
      });
    }
  };
  return (
    <View style={styles.container}>
      <TouchableOpacity style={styles.playBtn} onPress={playPause}>
        <Ionicons
          name={playing ? 'ios-pause-outline' : 'ios-play-outline'}
          size={36}
          color={'#fff'}
        />
      </TouchableOpacity>
    </View>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#000',
  },
  playBtn: {
    padding: 20,
  },
});
export default App;

.release() is used to release the memory reserved to play the audio files. It’s very important to release the memory after the component is unmounted or the audio is not to be played again. This helps to avoid memory leaks or other errors.

Conclusion

react-native-sound is a great module for adding sounds or building a simple audio player for your React Native app.

In this tutorial, we went over how to add and play audio from a native bundle as well as from a remote file. If you want to explore more options in this regard, I would suggest trying out the [expo-av](https://docs.expo.dev/versions/latest/sdk/av/) module of Expo unimodules to play audio or video files in your React Native app.


You can also checkout this post over Logrocket here.