Quick start
This guide gets a track playing. It works in both Expo and bare React Native (New Architecture enabled).
1. Install
npm install react-native-queue-player react-native-nitro-modules
react-native-nitro-modules is a required peer dependency — the library is built on Nitro Modules. The library autolinks; there is nothing to register.
The minimum iOS deployment target is 16.0; on Android it is minSdk 24. Version 3 requires React Native 0.88 or later. With Expo, use version 3 (3.0.0 or later) on Expo SDK 58 and later. Expo SDK 58 starts React Native from its scene delegate when a window connects, and its app delegate no longer creates a window. Version 2's CarPlay scene setup takes the phone window's content from the app delegate's window, so it does not work on SDK 58, and version 2 is not supported on SDK 58 in any configuration. Version 3 is built for SDK 58's start-up and, with the carplay or siri option, also starts JavaScript for CarPlay and Siri when no phone window is open.
On Expo SDK 57 and earlier, or React Native older than 0.88, use 2.0.4-rc.6, the last version 2 release (tested with Expo SDK 57 and React Native 0.86). It is a prerelease, so install it by exact version: npm install react-native-queue-player@2.0.4-rc.6. See Upgrading to v3.
2. Native setup
Android needs no manual native config in either workflow — the library ships its own manifest, merged in automatically. iOS needs a little setup, which differs by workflow.
Expo
Add the config plugin to your app config:
{
"expo": {
"plugins": [
["react-native-queue-player", { "carplay": false, "siri": false, "chromecast": false }]
]
}
}
For basic playback leave all three flags false — enable them when you add CarPlay, Siri / voice, or Chromecast. The plugin sets the iOS background-audio mode automatically, along with the Info.plist keys, entitlements, and AppDelegate and SceneDelegate code for any integration you enable. Then regenerate native projects, and again whenever you change the plugin's options:
npx expo prebuild --clean
Bare React Native
There's no plugin step. Add the one required iOS key — background audio — to ios/<YourApp>/Info.plist, then install pods:
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
cd ios && pod install
That's everything for basic playback. See Setup & manual configuration for the full per-feature native setup (CarPlay, Siri, Chromecast) in both workflows.
3. Configure the player from a bootstrap module
getTrackPlayer() returns a singleton — the same instance every time; there is no class to construct. Always call configure() once, early, from a dedicated bootstrap module that runs before your app's UI:
// src/playerBootstrap.ts
import { getTrackPlayer } from 'react-native-queue-player';
void getTrackPlayer().configure({ audioContentType: 'music' });
// index.js — import the bootstrap BEFORE your app entry so it always runs first
import './src/playerBootstrap';
import 'expo-router/entry'; // or your bare-RN AppRegistry entry
This is the recommended pattern for every app: it guarantees the player is ready on every launch, including cold starts where the system (a lock-screen widget, CarPlay, Android Auto, or an assistant) can wake your JavaScript before any screen mounts. See Setup & manual configuration for the full pattern, including registerPlaybackService() for automotive / voice.
4. Load a queue and play
Each track needs only a url (http(s):// or file://). All other fields are optional metadata.
const player = getTrackPlayer();
await player.setQueue([
{ url: 'https://stream.example/track1.mp3', title: 'One', artist: 'Artist', album: 'Album', duration: 180 },
{ url: 'https://stream.example/track2.mp3', title: 'Two', artist: 'Artist', album: 'Album', duration: 200 },
]);
await player.play();
Transport is a handful of promises:
await player.pause();
await player.skipToNext();
await player.skipToPrevious();
await player.skipToIndex(0);
await player.seekTo(30); // seconds into the current track
await player.setVolume(0.8); // 0.0 – 1.0
5. Build your UI with hooks
Hooks subscribe to the native event streams directly:
import {
useActiveTrack,
usePlaybackState,
useProgress,
useCanSkip,
} from 'react-native-queue-player';
function NowPlaying() {
const { track } = useActiveTrack();
const { state } = usePlaybackState(); // 'playing' | 'paused' | 'buffering' | …
const { position, duration } = useProgress(); // seconds
const { canSkipNext, canSkipPrevious } = useCanSkip();
if (track == null) return <Text>No track loaded</Text>;
return (
<View>
<Text>{track.title} — {track.artist}</Text>
<Text>{state} · {position.toFixed(0)} / {duration.toFixed(0)}s</Text>
</View>
);
}
Next steps
- Setup & manual configuration — background audio, plugin flags, the bootstrap init, runtime permissions.
- Custom HTTP headers — auth tokens and a custom user agent for your media.
- API reference — the full surface, including playback modes, repeat, and shuffle.