Player Setup

Configure and initialize the player before any other API calls.

setupPlayer

TrackPlayer.setupPlayer(options?:PlayerConfig):void

Initializes the native player and applies initial options. Must be called once before using any other API. Defaults are defined in TS; native receives a full config. - **Android**: Call it from registerPlaybackSession when remote handlers or background and car starts matter; handlers only take over after setup in the same runtime. - **Android**: capabilities and skip intervals keep the values from the last setCommands call across app launches and headless starts. Setup resets handling to `'native'` and clears per-command handling so transport works without JS listeners. Until the first `setCommands`, capabilities are PlayPause only and skip intervals are 15 seconds. - **Android**: controller startup is asynchronous. Commands issued during startup are buffered in order without an operation limit. A 10-second deadline covers connection and configuration acknowledgement; draining buffered work has no deadline. A connection or configuration failure discards pending work, releases the connection, and emits Event.PlaybackError with code `'controller-connection-failed'`. Call `destroy()` then `setupPlayer()` to recover. - **Android**: playback and queue getters return defaults until startup finishes. Do not gate startup writes on these reads: an empty `getQueue()` during startup does not mean the service has no queue. For example, playback started by Android Auto may already be active. Issue intended startup writes without conditional startup reads, and use playback events to observe subsequent state changes. - **Android**: after startup, synchronous JS getters wait for earlier calls on the native module queue before reading the controller on main. Player mutations such as `clear(); add(item); getQueue()` are visible in that read. Service operations awaiting media resolution or a custom-command barrier can still be pending. Native callers already on main read immediately to avoid blocking that queue. - **Android**: `destroy()` discards buffered work while connecting. After startup it runs after pending commands, so `pause(); clear(); destroy()` delivers the clear before release. - **iOS with CarPlay**: Start the React Native host without a window in `application(_:didFinishLaunchingWithOptions:)` using `factory.rootViewFactory.initializeReactHost(withLaunchOptions:devMenuConfiguration:)`; attach the phone window with `startReactNative` in the scene delegate. - Later calls in the same JS runtime are ignored (with a development warning)

FieldTypeDefaultDescription
contentType 'music' | 'speech'—Content type hint for the system. Affects audio routing and focus behavior. - 'music': Standard music playback (default) - 'speech': Spoken content like podcasts/audiobooks On iOS this sets the AVAudioSession mode (.default vs .spokenAudio). On Android this sets AudioAttributes contentType (MUSIC vs SPEECH).
handleAudioBecomingNoisy boolean—Whether the player should automatically pause when audio output is rerouted (e.g. headphones disconnected). Defaults to true.
autoUpdateMetadataFromStream v5.1.0+booleantrueWhen `true` (default), ICY/ID3 metadata updates the queued MediaItem and Now Playing. When `false`, only Event.MetadataReceived is emitted; call updateMetadata yourself.
audioMixing v5.5.0+'exclusive' | 'mix''exclusive'How this player's audio coexists with other apps' audio. - 'exclusive' (default): interrupts other apps' audio. - 'mix': plays layered over other apps without interrupting them. On iOS this toggles the AVAudioSession `.mixWithOthers` category option. On Android 'mix' holds no audio focus, so the player won't auto-pause for calls or resume afterwards.
liveResumeBehavior v5.7.0+LiveResumeBehavior'position'Where an isLive item resumes on an explicit Play request.
cache CacheConfig—Enable disk caching and preloading. When set, played audio is written through to a disk cache and the next queue item is automatically preloaded. Previously cached content is served from disk on subsequent plays. On Android this uses Media3's SimpleCache + CacheDataSource. On iOS this uses an AVAssetResourceLoaderDelegate-based streaming cache. On web, caching, `preload`, and `clearCache` are inactive no-ops, and Progress.cached is always `0`.
progressSync ProgressSyncConfig——
android Partial<AndroidPlayerConfig>——

CacheConfig

FieldTypeDefaultDescription
maxSizeBytes number524288000 (500 MB)Maximum disk cache size in bytes. When the cache exceeds this size, least-recently-used items are evicted.
preloading PreloadConfig—Preloading configuration. Requires cache to be enabled.

ProgressSyncConfig

FieldTypeDefaultDescription
intervalSeconds number——
http { url: string; headers?: Record<string, string> }——

The HTTP POST body is JSON with the following shape:

{ "mediaId": "ep-123", "position": 142.5, "duration": 3600.0, "timestamp": 1710000000000 }

This is the same payload emitted by the PlaybackProgressUpdated event. A final tick is always sent on pause or stop.

AndroidPlayerConfig

FieldTypeDefaultDescription
wakeMode * 'none' | 'local' | 'network''none'—
skipSilenceEnabled * booleanfalse—
taskRemovedBehavior v5.4.0+TaskRemovedBehavior'continue'When the app is removed from recents: `continue` keeps playback; `stop` tears down the service.
autoStartJs v5.10.0+booleanfalseOpt in to loading the React Native entry bundle when the Android playback service has no JavaScript runtime and either retains a replayable playback event or receives an eligible media-library root request. RNTP supports the New Architecture with bridgeless enabled only. Bridged Fabric and legacy architecture configurations are unsupported. Cold-start JavaScript boot uses the app's ReactHost, exposed by its Application. If no ReactHost is available, the service logs a warning and skips boot. The entry file must be safe to run headlessly. Keep every service registered there, including registerPlaybackSession, UI-free through its entire import graph: it must not transitively import the app component, navigation, screens, or UI components. Load the app component only from the `AppRegistry.registerComponent` provider. Otherwise opening the app during a car session can crash the Activity after the headless runtime has started. Module-scope service code must not touch the UI, and `currentActivity` is `null`. Library-root startup excludes this app's own controllers, untrusted third-party browsers, and SystemUI recent-content requests unless Media3 classifies the browser as Android Auto or Android Automotive OS. Root lookup never waits for JavaScript: persisted browse content remains immediately available, and clients are notified only if headless code later publishes a new tree. Neither network completion nor client re-query is guaranteed. Playback-event delivery is also best-effort because the native backlog is bounded. If the bundle fails to load, the app's React Native error policy applies. Publishing browse content for cold start: publish the last-known tree synchronously from registerPlaybackSession only when `getBrowseTreeInfo().isEmpty` or you have newer content. Fetch fresh content in a listener for Event.BrowseRefreshRequested, which runs inside a Headless JS task with live timers. Android emits at most one refresh per 15 seconds across all car controllers. In a headless-booted runtime, timers including `setTimeout(0)` stay paused unless a Headless JS task is active, such as during playback-event delivery. React Native's `fetch` resolves through a timer, so async refreshes started from the session stall until a task runs; timers pause again when it finishes.
notification { channelId: string; channelName: string; smallIcon: string; }—Android notification configuration.
cast string—Chromecast receiver app ID. When set, Cast support is enabled and the `<OutputDeviceButton>` / `<CastButton>` will filter for this receiver. Use `DEFAULT_CAST_RECEIVER_APP_ID` for Google's Default Media Receiver (no registration required). Omit to disable Cast entirely.

notification is an object with channelId, channelName, and smallIcon (Android resource name, e.g. 'ic_stat_music_note').

taskRemovedBehavior (continue | stop, default continue) controls whether playback survives when the app is swiped from recents.

Example

TrackPlayer.setupPlayer({
  contentType: 'music',
  handleAudioBecomingNoisy: true,
  cache: { maxSizeBytes: 200 * 1024 * 1024 }, // 200 MB
  progressSync: {
    intervalSeconds: 30,
    http: {
      url: 'https://api.example.com/progress',
      headers: { 'Authorization': 'Bearer xxx' },
    },
  },
  android: {
    wakeMode: 'network',
    notification: {
      channelId: 'com.myapp.audio',
      channelName: 'Audio Playback',
      smallIcon: 'ic_notification',
    },
  },
});

destroy

TrackPlayer.destroy():void

Tears down the player and releases all resources. After calling this, you must call `setupPlayer()` again to use the player. Runtime event subscriptions and remote-handler registrations survive destroy. On web, `destroy()` no longer removes listeners; call each subscription's `remove()` before registering it again after setup. Remote handlers resume after setup with the retained capabilities and skip intervals. Android setup resets legacy command handling to native; iOS and web preserve handling and per-command handling. Android discards buffered commands while connecting. After startup, commands issued before `destroy()` finish before the controller connection is released.

Tears down the player and releases all resources. After calling this, you must call setupPlayer() again to use the player.

TrackPlayer.destroy();

setCommands

Configure which commands appear on the lock screen, notification, and native media session (including Android Auto when the host connects to your playback service).

TrackPlayer.setCommands(commands:RemoteControlConfig):void

Update remote control configuration at runtime. Use this to change which commands appear in the notification/lock screen (e.g., disable skip buttons on a single-item queue). Configures the **native** media session (lock screen, notification, Android Auto). With `handling: 'native'` (default), actions run without JavaScript. `handling: 'js'` / `'hybrid'` emit remote events when a usable runtime exists. A remote command never starts a cold Android runtime; JS-authority commands fall back to native behavior in that case and are not replayed later. Android persists capabilities and skip intervals for cold playback-service starts. Handling and per-command handling stay in memory and apply only while the owning RNTP controller is connected. System Previous/Next, including notification overflow buttons and media keys, run natively under legacy handling. On Android, `setupPlayer` preserves capabilities and skip intervals, but resets handling to `'native'` and clears `perCommandHandling`. iOS and web retain both legacy handling settings across setup. On Android and iOS, optional fields omitted from a `setCommands` call keep their previous value; on web, each call replaces the whole configuration. To reset them, pass them explicitly, e.g. `setCommands({ capabilities: [PlayerCommand.PlayPause], handling: 'native', forwardInterval: 15, backwardInterval: 15 })`. When switching to `'hybrid'`, pass `perCommandHandling`; otherwise any map retained since the last setup applies.

Remote control and media-session command configuration (lock screen, notification, Android Auto, and similar surfaces). This configures **native** behavior. With `handling: 'native'` (default), actions run without JavaScript. Android persists capabilities and skip intervals; handling and per-command handling belong to the current runtime and are never persisted. `handling: 'js'` / `'hybrid'` emit events when a usable runtime exists. Remote commands never start a cold Android runtime; a cold JS-authority command instead executes natively and is not replayed to JavaScript later.

FieldTypeDefaultDescription
capabilities * PlayerCommand[]—Which commands are available in the notification/remote controls and media session.
handling RemoteControlHandling'native'Who handles remote control button presses.
perCommandHandling PerCommandHandling—Per-command handling overrides (only used when handling is 'hybrid').
forwardInterval number—Interval in seconds for the skip-forward button (default: 15). Native handling seeks forward by this amount, and `RemoteSkipForward` events report it as `interval`. A change through `setCommands` applies from the next press.
backwardInterval number—Interval in seconds for the skip-backward button (default: 15). Native handling seeks back by this amount, and `RemoteSkipBackward` events report it as `interval`. A change through `setCommands` applies from the next press.

To run your own logic when a button is pressed, register remote handlers. The handling and perCommandHandling options below are deprecated and removed in 6.0; on Android, setupPlayer resets them to native. On web, each setCommands call replaces the whole configuration, so pass every option you want to keep. In-car behavior: Remote controls in-car.

Available PlayerCommand values: PlayerCommand.Seek, PlayerCommand.PlayPause, PlayerCommand.Next, PlayerCommand.Previous, PlayerCommand.Stop, PlayerCommand.SkipForward, PlayerCommand.SkipBackward.

import { PlayerCommand } from '@rntp/player';

TrackPlayer.setCommands({
  capabilities: [
    PlayerCommand.PlayPause,
    PlayerCommand.Next,
    PlayerCommand.Previous,
    PlayerCommand.Seek,
  ],
  forwardInterval: 30,
  backwardInterval: 15,
});

updateProgressSyncHeaders

TrackPlayer.updateProgressSyncHeaders(headers:Record<string, string>):void

Update the HTTP headers used for progress sync POST requests.

Update the HTTP headers used for progress sync POST requests at runtime — useful when auth tokens refresh.

TrackPlayer.updateProgressSyncHeaders({ Authorization: 'Bearer new-token' });

Caching

When cache is configured, audio is stored locally after the first download. Subsequent plays of the same URL are served from cache without a network request.

TrackPlayer.setupPlayer({
  cache: { maxSizeBytes: 500 * 1024 * 1024 }, // 500 MB
});
TrackPlayer.clearCache():void

Clears the on-disk audio cache. For debugging; next playback will re-download.

TrackPlayer.clearCache();
The useProgress hook exposes a cached field (in seconds) indicating how much of the current track is stored locally. Caching works regardless of whether the server provides a Content-Length header — the total size is determined when the download completes.

Preloading

Preloading downloads tracks to cache before they’re played, reducing gaps between tracks and enabling instant playback from browse screens.

Auto preloading

Automatically preload the next tracks in the queue:

FieldTypeDefaultDescription
window number0 (disabled)Auto-preload next N tracks in queue. Preloading starts when the current item is live, a local file, or fully cached on disk. It also re-evaluates when the queue changes (items added, removed, replaced, or reordered).
TrackPlayer.setupPlayer({
  cache: {
    preloading: {
      window: 1, // preload next 1 track
    },
  },
});

Auto preloading starts when the current track is fully cached, is a live stream, or is a local file. It also re-evaluates when the queue changes (items added, removed, replaced, or reordered). For on-demand streams, preloading waits until the current track finishes downloading to avoid competing for bandwidth.

Manual preloading

Preload any track on demand — useful for browse screens, recommendations, or any UI where the user might play a track:

TrackPlayer.preload(item:MediaItem,options?:PreloadOptions):void

Preload a media item into the cache for instant playback later. Works independently of the queue — any future play of the same URL will be a cache hit.

TrackPlayer.cancelPreload(item:MediaItem):void

Cancel an in-progress preload for the given media item. Completed data may remain cached.

FieldTypeDefaultDescription
duration number—Target duration in seconds. If omitted, preloads the entire file.
// Preload entire file
TrackPlayer.preload(mediaItem);

// Preload approximately 30 seconds
TrackPlayer.preload(mediaItem, { duration: 30 });

// Cancel an in-progress preload
TrackPlayer.cancelPreload(mediaItem);

Manual preloading works independently of the queue. The cache is keyed by URL, so any future play of the same URL will be a cache hit.

Preloading requires cache to be configured in setupPlayer.
ende