expo-av vs react-native-sound vs react-native-track-player
Architecting Audio Solutions in React Native: From Simple Effects to Background Music
expo-avreact-native-soundreact-native-track-playerSimilar Packages:

Architecting Audio Solutions in React Native: From Simple Effects to Background Music

expo-av, react-native-sound, and react-native-track-player are the primary libraries for handling audio in React Native, but they solve distinctly different problems. expo-av is a comprehensive, managed module within the Expo ecosystem that handles both audio and video, offering a balanced API for playback, recording, and streaming without native code configuration. react-native-sound is a lightweight, community-maintained library focused strictly on playing short sound effects and UI feedback with minimal setup, though it lacks advanced streaming or background capabilities. react-native-track-player is a specialized, robust solution designed specifically for music players and podcasts, providing full support for background playback, lock screen controls, and remote handling via native media sessions.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
expo-av052,4301.3 MB91110 months agoMIT
react-native-sound02,91894.4 kB254a year agoMIT
react-native-track-player03,705387 kB18a year agoApache-2.0

Architecting Audio Solutions in React Native: From Simple Effects to Background Music

Choosing the right audio library in React Native is not just about picking a feature list; it is about matching the library's architectural model to your app's lifecycle requirements. expo-av, react-native-sound, and react-native-track-player represent three distinct approaches: the all-in-one managed solution, the lightweight effect player, and the dedicated background service engine. Let's break down how they handle real-world engineering challenges.

๐Ÿ—๏ธ Setup and Native Integration

The barrier to entry varies significantly depending on your project structure and how much native control you need.

expo-av is built for the Managed Workflow.

  • If you use Expo Go, it works instantly with zero config.
  • For custom native code (Development Client), you only need to add the config plugin.
// expo-av: Import and use directly
import { Audio } from 'expo-av';

async function playSound() {
  const { sound } = await Audio.Sound.createAsync(
    require('./assets/hello.mp3')
  );
  await sound.playAsync();
}

react-native-sound requires manual linking or CocoaPods/Gradle setup.

  • You must configure native project files manually if not using auto-linking.
  • It does not support Expo Go without ejecting.
// react-native-sound: Requires native setup
import Sound from 'react-native-sound';

Sound.setCategory('Playback');
const whoosh = new Sound('whoosh.mp3', Sound.MAIN_BUNDLE, (error) => {
  if (error) {
    console.log('failed to load the sound', error);
    return;
  }
  whoosh.play();
});

react-native-track-player demands the most setup.

  • You must configure native capabilities (Background Modes) in Xcode and AndroidManifest.
  • It requires registering a separate JavaScript service thread to handle events when the app is suspended.
// react-native-track-player: Setup in index.js
import TrackPlayer from 'react-native-track-player';

export async function PlaybackService() {
  TrackPlayer.addEventListener('remote-play', () => TrackPlayer.play());
  TrackPlayer.addEventListener('remote-pause', () => TrackPlayer.pause());
}

TrackPlayer.registerPlaybackService(() => PlaybackService);

๐ŸŽต Playback Architecture: Loops vs. Streams

How the library manages the audio engine dictates what kind of content you can play.

expo-av uses a high-level Sound object that manages its own lifecycle.

  • Great for looping short clips or playing single files.
  • Handles streaming URLs but keeps the connection tied to the app's foreground lifecycle by default.
// expo-av: Looping a sound
const { sound } = await Audio.Sound.createAsync(
  { uri: 'https://example.com/stream.mp3' },
  { isLooping: true }
);
await sound.playAsync();

react-native-sound loads the entire file into memory or streams simply.

  • Designed for low-latency triggers.
  • Not optimized for long streaming sessions; the connection may drop if the app backgrounds.
// react-native-sound: Simple playback
const music = new Sound('music.mp3', Sound.MAIN_BUNDLE);
music.play((success) => {
  if (success) {
    console.log('successfully finished playing');
  } else {
    console.log('playback failed due to audio decoding errors');
  }
});

react-native-track-player utilizes a native background queue.

  • You add tracks to a queue, and the native module handles buffering and switching.
  • Keeps playing even if the app is killed or the phone is locked.
// react-native-track-player: Queue management
import TrackPlayer from 'react-native-track-player';

await TrackPlayer.setupPlayer();
await TrackPlayer.add({
  id: 'track1',
  url: 'https://example.com/song.mp3',
  title: 'My Song',
  artist: 'Artist Name'
});
await TrackPlayer.play();

๐Ÿ“ฑ Background Execution and Lock Screen Controls

This is the most critical differentiator for music and podcast apps.

expo-av has limited background support out of the box.

  • In Managed Workflow, background audio often stops when the screen locks unless you configure specific capabilities in app.json and use a Development Client.
  • Lock screen controls are not automatically generated; you often need extra modules.
// expo-av: Limited native control exposure
// No direct API to update lock screen metadata like 'Next' button handlers
// without additional native modules or ejecting.
await sound.setStatusAsync({ shouldPlay: true });

react-native-sound does not support background playback.

  • Audio stops immediately when the app loses focus or the screen locks.
  • There are no APIs for lock screen metadata.
// react-native-sound: No background capability
// If the user presses the home button, audio stops.
// No code exists to enable lock screen controls.

react-native-track-player is built specifically for this.

  • You define "capabilities" (Play, Pause, Skip) that appear on the lock screen and car displays.
  • The PlaybackService runs in a separate thread, surviving app suspension.
// react-native-track-player: Updating metadata and capabilities
await TrackPlayer.updateOptions({
  capabilities: [
    TrackPlayer.CAPABILITY_PLAY,
    TrackPlayer.CAPABILITY_PAUSE,
    TrackPlayer.CAPABILITY_SKIP_TO_NEXT,
  ],
  compactCapabilities: [TrackPlayer.CAPABILITY_PLAY, TrackPlayer.CAPABILITY_PAUSE],
});

await TrackPlayer.updateMetadata({
  title: 'New Song',
  artist: 'New Artist',
  // Updates lock screen and control center immediately
});

๐ŸŽ™๏ธ Recording Capabilities

Some apps need to capture audio, not just play it.

expo-av provides a complete Recording API.

  • Handles permissions, configuration (quality, extension), and status updates.
  • Returns a URI to the saved file ready for upload or playback.
// expo-av: Recording audio
const { recording } = await Audio.Recording.createAsync(
   Audio.RecordingOptionsPresets.HIGH_QUALITY
);
// Stop and get file URI
await recording.stopAndUnloadAsync();
const uri = recording.getURI(); 

react-native-sound has no recording features.

  • It is playback-only.
  • You would need a third library (like react-native-audio-recorder-player) to add this.
// react-native-sound: No recording API
// Cannot record audio with this library.

react-native-track-player does not support recording.

  • It is strictly a player.
  • Mixing it with a recorder requires careful management of two different audio sessions.
// react-native-track-player: No recording API
// Focused purely on playback queue management.

๐ŸŒฑ Similarities: Shared Ground

Despite their differences, these libraries share some common React Native patterns.

1. ๐Ÿ”Š Volume Control

All three allow you to adjust volume programmatically, though the scope differs.

// expo-av
await sound.setVolumeAsync(0.5);

// react-native-sound
whoosh.setVolume(0.5);

// react-native-track-player
await TrackPlayer.setVolume(0.5);

2. ๐Ÿ”„ Event Listeners

Each library provides a way to know when a sound finishes.

// expo-av
sound.setOnPlaybackStatusUpdate((status) => {
  if (status.isLoaded && status.didJustFinish) { /* ... */ }
});

// react-native-sound
whoosh.play((success) => { if(!success) { /* finished or error */ } });

// react-native-track-player
TrackPlayer.addEventListener('playback-track-changed', (data) => { /* ... */ });

3. ๐Ÿ“‚ Asset Handling

All support loading from local bundles and remote URLs.

// All three support this pattern conceptually
// expo-av: require('./local.mp3') or { uri: 'https://...' }
// react-native-sound: new Sound('file.mp3', Sound.MAIN_BUNDLE)
// react-native-track-player: url: 'https://...' or require('./local.mp3')

๐Ÿ“Š Summary: Key Differences

Featureexpo-avreact-native-soundreact-native-track-player
Primary Use CaseGeneral purpose (Video + Audio)UI Sound EffectsMusic Players / Podcasts
Background PlaybackLimited (Requires Config Client)โŒ Not Supportedโœ… Fully Supported
Lock Screen ControlsโŒ Manual/ComplexโŒ Not Supportedโœ… Native Integration
Recordingโœ… Built-inโŒ NoneโŒ None
Expo Go Supportโœ… YesโŒ NoโŒ No
ComplexityMediumLowHigh

๐Ÿ’ก The Big Picture

expo-av is the pragmatic choice for most standard apps.
If you need to record voice notes, play video, or handle simple audio within an Expo project, this is your default. It balances power with ease of use, keeping you in the managed workflow until you absolutely need more.

react-native-sound is the specialist for micro-interactions.
Use it when you need a button click to feel crisp without the overhead of a full audio engine. It is perfect for games or interactive UIs where latency matters more than features. If you try to build a music player with this, you will fight the architecture at every step.

react-native-track-player is the heavy lifter for media apps.
If your app's core value is audio content (music, audiobooks, radio), this is the only professional choice. The initial setup cost is higher, but it saves you from rebuilding native background services and media session handlers yourself. It ensures your app behaves like a first-class citizen on iOS and Android lock screens.

Final Thought: Don't over-engineer a simple sound effect with react-native-track-player, and don't under-engineer a music player with react-native-sound. Match the tool to the lifecycle requirements of your audio.

How to Choose: expo-av vs react-native-sound vs react-native-track-player

  • expo-av:

    Choose expo-av if you are building within the Expo Managed Workflow or need a single library to handle both audio playback/recording and video. It is the best fit for applications requiring voice memos, video chats, or mixed media where avoiding custom native code is a priority. However, be aware that advanced background audio features may require ejecting to the Development Client or configuring specific capabilities manually.

  • react-native-sound:

    Choose react-native-sound if your needs are limited to playing short, local sound effects like button clicks, notifications, or game SFX. It is ideal for projects where you want a tiny footprint and simple fire-and-forget playback without the complexity of streaming or background services. Do not use this for music players, long-form content, or apps requiring lock screen controls, as it lacks these architectural features.

  • react-native-track-player:

    Choose react-native-track-player if you are building a music player, podcast app, or any service requiring continuous background audio and lock screen controls. It is the only choice among the three that fully implements native Media Session APIs, allowing users to control playback from the system UI, car displays, and smartwatches. Be prepared for a more complex setup involving native configuration files and a separate JavaScript service thread.