Skip to main content

Events

React Track Player Web emits various events that allow you to monitor and respond to changes in playback status, track changes, and more.

Available Events​

The library provides the following events, accessible through the Event enum:

EventDescription
PlaybackStateFired when the playback state changes (playing, paused, etc.)
PlaybackTrackChangedFired when the current track changes
PlaybackProgressUpdatedFired periodically with updated playback position
PlaybackErrorFired when a playback error occurs

Listening to Events​

You can register event listeners using the addEventListener method:

import TrackPlayer, { Event } from "react-track-player-web"

// Listen for playback state changes
const stateListener = (data) => {
console.log("Playback state changed:", data.state)
}
TrackPlayer.addEventListener(Event.PlaybackState, stateListener)

// Listen for track changes
TrackPlayer.addEventListener(Event.PlaybackTrackChanged, (data) => {
console.log("Track changed from", data.prevTrack, "to", data.nextTrack)
})

// Listen for progress updates
TrackPlayer.addEventListener(Event.PlaybackProgressUpdated, (data) => {
console.log(`Position: ${data.position}, Duration: ${data.duration}`)
})

// Listen for errors
TrackPlayer.addEventListener(Event.PlaybackError, (data) => {
console.error("Playback error:", data.error, "Code:", data.code)
})

Removing Event Listeners​

To prevent memory leaks, you should remove event listeners when they're no longer needed:

// Remove a specific listener
TrackPlayer.removeEventListener(Event.PlaybackState, stateListener)

Event Data​

Each event provides specific data:

PlaybackState​

{
type: Event.PlaybackState,
state: State // The new playback state
}

PlaybackTrackChanged​

{
type: Event.PlaybackTrackChanged,
prevTrack: number | null, // Index of the previous track (or null)
nextTrack: number | null // Index of the new track (or null)
}

PlaybackProgressUpdated​

{
type: Event.PlaybackProgressUpdated,
position: number, // Current playback position in seconds
duration: number, // Track duration in seconds
buffered: number // Buffered position in seconds
}

PlaybackError​

{
type: Event.PlaybackError,
error: string, // Error message
code?: string // Error code (if available)
}

Advanced Event Handling Examples​

Auto-saving Playback Position​

Save the current playback position periodically to localStorage:

import { Event } from "react-track-player-web"

TrackPlayer.addEventListener(Event.PlaybackProgressUpdated, (data) => {
// Save position every 10 seconds
if (Math.floor(data.position) % 10 === 0) {
localStorage.setItem('lastPosition', data.position.toString())
localStorage.setItem('lastTrackIndex', TrackPlayer.getActiveTrackIndex().toString())
}
})

Track Analytics​

Track user listening behavior:

import { Event, State } from "react-track-player-web"

let trackStartTime = null
let totalListenTime = 0

TrackPlayer.addEventListener(Event.PlaybackTrackChanged, (data) => {
if (data.prevTrack !== null) {
// Send analytics for previous track
const track = TrackPlayer.getTrack(data.prevTrack)
if (track && totalListenTime > 0) {
console.log(`Track "${track.title}" listened for ${totalListenTime} seconds`)
// Send to analytics service
}
}

// Reset for new track
trackStartTime = Date.now()
totalListenTime = 0
})

TrackPlayer.addEventListener(Event.PlaybackState, (data) => {
if (data.state === State.Playing && trackStartTime) {
// Resume tracking
} else if (data.state === State.Paused && trackStartTime) {
// Add to total listen time
totalListenTime += (Date.now() - trackStartTime) / 1000
}
})

Queue Management​

Automatically load more tracks when nearing the end of the queue:

import { Event } from "react-track-player-web"

TrackPlayer.addEventListener(Event.PlaybackTrackChanged, async (data) => {
const queue = TrackPlayer.getQueue()
const currentIndex = data.nextTrack

// If we're near the end of the queue, load more tracks
if (currentIndex !== null && currentIndex >= queue.length - 2) {
try {
const moreTracks = await fetchMoreTracks()
await TrackPlayer.add(moreTracks)
console.log(`Added ${moreTracks.length} more tracks to queue`)
} catch (error) {
console.error('Failed to load more tracks:', error)
}
}
})

Error Recovery​

Implement automatic error recovery with exponential backoff:

import { Event } from "react-track-player-web"

let retryCount = 0
const MAX_RETRIES = 3

TrackPlayer.addEventListener(Event.PlaybackError, async (data) => {
console.error(`Playback error: ${data.error}`)

if (retryCount < MAX_RETRIES) {
const delay = Math.pow(2, retryCount) * 1000 // Exponential backoff
retryCount++

console.log(`Retrying in ${delay}ms (attempt ${retryCount}/${MAX_RETRIES})`)

setTimeout(async () => {
try {
await TrackPlayer.retry()
retryCount = 0 // Reset on success
} catch (error) {
console.error('Retry failed:', error)
}
}, delay)
} else {
// Max retries reached, skip to next track
console.log('Max retries reached, skipping to next track')
try {
await TrackPlayer.skipToNext()
retryCount = 0 // Reset for next track
} catch (error) {
console.error('Failed to skip to next track:', error)
}
}
})

// Reset retry count on successful track change
TrackPlayer.addEventListener(Event.PlaybackTrackChanged, () => {
retryCount = 0
})

Real-time UI Updates​

Create a comprehensive player status component:

import React, { useState } from 'react'
import { useTrackPlayerEvents, Event, State } from 'react-track-player-web'

function PlayerStatus() {
const [status, setStatus] = useState({
state: 'Unknown',
track: null,
progress: { position: 0, duration: 0, buffered: 0 },
error: null
})

useTrackPlayerEvents([
Event.PlaybackState,
Event.PlaybackTrackChanged,
Event.PlaybackProgressUpdated,
Event.PlaybackError
], (event) => {
switch (event.type) {
case Event.PlaybackState:
setStatus(prev => ({
...prev,
state: event.state,
error: event.state === State.Error ? prev.error : null
}))
break

case Event.PlaybackTrackChanged:
const track = event.nextTrack !== null ? TrackPlayer.getTrack(event.nextTrack) : null
setStatus(prev => ({ ...prev, track }))
break

case Event.PlaybackProgressUpdated:
setStatus(prev => ({ ...prev, progress: event }))
break

case Event.PlaybackError:
setStatus(prev => ({ ...prev, error: event.error }))
break
}
})

return (
<div className="player-status">
<div>State: {status.state}</div>
{status.track && (
<div>Track: {status.track.title} by {status.track.artist}</div>
)}
<div>
Progress: {Math.floor(status.progress.position)}s / {Math.floor(status.progress.duration)}s
</div>
{status.error && (
<div style={{ color: 'red' }}>Error: {status.error}</div>
)}
</div>
)
}

Using with React Components​

Instead of manually managing event listeners in React components, it's recommended to use the useTrackPlayerEvents hook:

import React, { useState } from "react"
import { useTrackPlayerEvents, Event, State } from "react-track-player-web"

function PlayerStatus() {
const [playerState, setPlayerState] = useState("Unknown")
const [currentTrack, setCurrentTrack] = useState(null)

// Register multiple event listeners at once
useTrackPlayerEvents([Event.PlaybackState, Event.PlaybackTrackChanged], (event) => {
if (event.type === Event.PlaybackState) {
// Map state enum to human-readable string
const stateMap = {
[State.None]: "Uninitialized",
[State.Ready]: "Ready",
[State.Playing]: "Playing",
[State.Paused]: "Paused",
[State.Stopped]: "Stopped",
[State.Buffering]: "Buffering",
[State.Error]: "Error"
}
setPlayerState(stateMap[event.state] || "Unknown")
}

if (event.type === Event.PlaybackTrackChanged) {
// Get the track information
const trackInfo = TrackPlayer.getTrack(event.nextTrack)
setCurrentTrack(trackInfo)
}
})

return (
<div>
<div>Player state: {playerState}</div>
{currentTrack && (
<div>
Now playing: {currentTrack.title} by {currentTrack.artist || "Unknown Artist"}
</div>
)}
</div>
)
}

This approach ensures that event listeners are properly cleaned up when the component unmounts.

Event Timing Considerations​

PlaybackProgressUpdated Frequency​

The PlaybackProgressUpdated event frequency is controlled by the updateInterval option in setupPlayer:

// Update every 100ms for smooth progress bars
await TrackPlayer.setupPlayer({
updateInterval: 0.1
})

// Update every 5 seconds for less frequent updates
await TrackPlayer.setupPlayer({
updateInterval: 5
})

Event Order​

Events are emitted in a predictable order:

  1. When starting playback: PlaybackState → PlaybackTrackChanged → PlaybackProgressUpdated
  2. When skipping: PlaybackTrackChanged → PlaybackState → PlaybackProgressUpdated
  3. When an error occurs: PlaybackError → PlaybackState (Error)

Understanding this order helps in building reliable event handlers.