Offline Downloads
April 1, 2026 ยท View on GitHub
React Native Nitro Player supports downloading tracks and playlists for offline playback. This feature enables users to save music locally and play it without an internet connection.
Prerequisites
Android Setup
Add the following to your AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Required permissions -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<application>
<!-- Required for background downloads with notifications -->
<service
android:name="androidx.work.impl.foreground.SystemForegroundService"
android:foregroundServiceType="dataSync"
android:exported="false" />
</application>
</manifest>
Important
The SystemForegroundService with foregroundServiceType="dataSync" is required for downloads to work properly in the background and display download progress notifications.
iOS Setup
No additional setup required for iOS.
Quick Start
1. Configure Download Manager
import { DownloadManager } from 'react-native-nitro-player'
DownloadManager.configure({
maxConcurrentDownloads: 3,
autoRetry: true,
maxRetryAttempts: 3,
backgroundDownloadsEnabled: true,
downloadArtwork: true,
wifiOnlyDownloads: false,
storageLocation: 'private', // 'private' or 'public'
})
2. Download Tracks
import { DownloadManager } from 'react-native-nitro-player'
import type { TrackItem } from 'react-native-nitro-player'
const track: TrackItem = {
id: '1',
title: 'Song Title',
artist: 'Artist Name',
album: 'Album Name',
duration: 180.0,
url: 'https://example.com/song.mp3',
artwork: 'https://example.com/artwork.jpg',
}
// Download a single track
const downloadId = await DownloadManager.downloadTrack(track)
// Download a track as part of a playlist
const downloadId = await DownloadManager.downloadTrack(track, 'playlist-id')
3. Download Playlists
import { PlayerQueue, DownloadManager } from 'react-native-nitro-player'
const playlist = PlayerQueue.getPlaylist('playlist-id')
const downloadIds = await DownloadManager.downloadPlaylist(
playlist.id,
playlist.tracks
)
4. Monitor Download Progress
import { useDownloadProgress } from 'react-native-nitro-player'
function DownloadProgressView() {
const { progressList, overallProgress, isDownloading } = useDownloadProgress()
return (
<View>
<Text>Overall Progress: {Math.round(overallProgress * 100)}%</Text>
<Text>Downloading: {isDownloading ? 'Yes' : 'No'}</Text>
{progressList.map((progress) => (
<View key={progress.trackId}>
<Text>{progress.trackId}</Text>
<Text>Progress: {Math.round(progress.progress * 100)}%</Text>
<Text>State: {progress.state}</Text>
</View>
))}
</View>
)
}
React Hooks
useDownloadProgress(options?)
Track download progress for one or more tracks.
Options:
trackIds?: string[]- Track specific track IDsdownloadIds?: string[]- Track specific download IDsactiveOnly?: boolean- Only track active downloads
Returns:
progressMap: Map<string, DownloadProgress>- Map of trackId to progressprogressList: DownloadProgress[]- Array of all tracked progressoverallProgress: number- Overall progress (0-1)isDownloading: boolean- Whether any download is in progressgetProgress: (trackId: string) => DownloadProgress | undefined- Get progress for a specific track
Example:
import { useDownloadProgress } from 'react-native-nitro-player'
function TrackDownloadProgress({ trackId }: { trackId: string }) {
const { getProgress } = useDownloadProgress({ trackIds: [trackId] })
const progress = getProgress(trackId)
if (!progress) return null
return (
<View>
<Text>{Math.round(progress.progress * 100)}%</Text>
<ProgressBar progress={progress.progress} />
</View>
)
}
useDownloadedTracks()
Access all downloaded tracks and playlists.
Returns:
downloadedTracks: DownloadedTrack[]- All downloaded tracksdownloadedPlaylists: DownloadedPlaylist[]- All downloaded playlistsisTrackDownloaded: (trackId: string) => boolean- Sync: whethertrackIdappears in the last refreshed lists (not a disk probe; useDownloadManager.isTrackDownloadedfor that)isPlaylistDownloaded: (playlistId: string) => boolean- Sync: derived from cacheddownloadedPlaylistsisPlaylistPartiallyDownloaded: (playlistId: string) => boolean- Sync: derived from cacheddownloadedPlaylistsgetDownloadedTrack: (trackId: string) => DownloadedTrack | undefined- Get downloaded track infogetDownloadedPlaylist: (playlistId: string) => DownloadedPlaylist | undefined- Get downloaded playlist inforefresh: () => void- Refresh downloaded content listisLoading: boolean- Loading state
Example:
import { useDownloadedTracks } from 'react-native-nitro-player'
function DownloadedTracksView() {
const { downloadedTracks, isTrackDownloaded, refresh } = useDownloadedTracks()
return (
<View>
<Button title="Refresh" onPress={refresh} />
{downloadedTracks.map((track) => (
<View key={track.trackId}>
<Text>{track.originalTrack.title}</Text>
<Text>Size: {(track.fileSize / 1024 / 1024).toFixed(2)} MB</Text>
</View>
))}
</View>
)
}
DownloadManager API
Configuration
configure(config: DownloadConfig): void
Configure the download manager.
Config Options:
storageLocation?: 'private' | 'public'- Where to store downloadsmaxConcurrentDownloads?: number- Max simultaneous downloads (default: 3)autoRetry?: boolean- Auto-retry failed downloads (default: true)maxRetryAttempts?: number- Max retry attempts (default: 3)backgroundDownloadsEnabled?: boolean- Enable background downloads (default: true)downloadArtwork?: boolean- Download artwork images (default: true)customDownloadPath?: string | null- Custom download directorywifiOnlyDownloads?: boolean- Only download on WiFi (default: false)
getConfig(): DownloadConfig
Get current configuration.
Download Operations
downloadTrack(track: TrackItem, playlistId?: string): Promise<string>
Download a single track. Returns the download ID for tracking.
downloadPlaylist(playlistId: string, tracks: TrackItem[]): Promise<string[]>
Download an entire playlist. Returns array of download IDs.
Download Control
pauseDownload(downloadId: string): Promise<void>
Pause an active download.
resumeDownload(downloadId: string): Promise<void>
Resume a paused download.
cancelDownload(downloadId: string): Promise<void>
Cancel a download and remove partial files.
retryDownload(downloadId: string): Promise<void>
Retry a failed download.
pauseAllDownloads(): Promise<void>
Pause all active downloads.
resumeAllDownloads(): Promise<void>
Resume all paused downloads.
cancelAllDownloads(): Promise<void>
Cancel all downloads.
Download Status
getDownloadTask(downloadId: string): DownloadTask | null
Get download task information by download ID.
getActiveDownloads(): DownloadTask[]
Get all active download tasks.
getQueueStatus(): DownloadQueueStatus
Get overall download queue status.
Returns:
{
pendingCount: number
activeCount: number
completedCount: number
failedCount: number
totalBytesToDownload: number
totalBytesDownloaded: number
overallProgress: number // 0.0 to 1.0
}
isDownloading(trackId: string): boolean
Check if a track is currently downloading.
getDownloadState(trackId: string): DownloadState | null
Get download state for a track. States: 'pending', 'downloading', 'paused', 'completed', 'failed', 'cancelled'.
Downloaded Content Queries
These methods consult the download database / filesystem and return Promises (see new-version plan.md). getDownloadTask, getActiveDownloads, isDownloading, and getDownloadState remain synchronous and reflect in-memory task state only.
isTrackDownloaded(trackId: string): Promise<boolean>
Check if a track is downloaded on disk.
isPlaylistDownloaded(playlistId: string): Promise<boolean>
Check if all tracks in a playlist are downloaded.
isPlaylistPartiallyDownloaded(playlistId: string): Promise<boolean>
Check if at least one track in a playlist is downloaded.
getDownloadedTrack(trackId: string): Promise<DownloadedTrack | null>
Get downloaded track information.
getAllDownloadedTracks(): Promise<DownloadedTrack[]>
Get all downloaded tracks.
getDownloadedPlaylist(playlistId: string): Promise<DownloadedPlaylist | null>
Get downloaded playlist information.
getAllDownloadedPlaylists(): Promise<DownloadedPlaylist[]>
Get all downloaded playlists.
getLocalPath(trackId: string): Promise<string | null>
Get local file path for a downloaded track.
Deletion
deleteDownloadedTrack(trackId: string): Promise<void>
Delete a downloaded track and its files.
deleteDownloadedPlaylist(playlistId: string): Promise<void>
Delete all tracks in a downloaded playlist.
deleteAllDownloads(): Promise<void>
Delete all downloaded content.
Storage Management
getStorageInfo(): Promise<DownloadStorageInfo>
Get storage usage information.
Returns:
{
totalDownloadedSize: number // bytes
trackCount: number
playlistCount: number
availableSpace: number // bytes
totalSpace: number // bytes
}
syncDownloads(): Promise<number>
Validate all downloads and remove orphaned records. Resolves to the number of orphaned records cleaned up.
Playback Source Preference
setPlaybackSourcePreference(preference: PlaybackSource): void
Set playback source preference: 'auto', 'download', or 'network'.
'auto'- Use downloaded version if available, otherwise stream'download'- Only play downloaded tracks'network'- Always stream from network
getPlaybackSourcePreference(): PlaybackSource
Get current playback source preference.
getEffectiveUrl(track: TrackItem): Promise<string>
Get the effective URL for a track (local or network based on preference and availability). await the result.
Event Callbacks
onDownloadProgress(callback: (progress: DownloadProgress) => void): void
Listen to download progress updates.
DownloadManager.onDownloadProgress(progress => {
console.log(`${progress.trackId}: ${Math.round(progress.progress * 100)}%`)
})
onDownloadStateChange(callback: (downloadId, trackId, state, error?) => void): void
Listen to download state changes.
DownloadManager.onDownloadStateChange((downloadId, trackId, state, error) => {
console.log(`${trackId} is now ${state}`)
if (error) {
console.error('Download error:', error.message)
}
})
onDownloadComplete(callback: (downloadedTrack: DownloadedTrack) => void): void
Listen to download completion events.
DownloadManager.onDownloadComplete(downloadedTrack => {
console.log(`Downloaded: ${downloadedTrack.originalTrack.title}`)
})
Type Definitions
DownloadProgress
interface DownloadProgress {
trackId: string
downloadId: string
bytesDownloaded: number
totalBytes: number
progress: number // 0.0 to 1.0
state: DownloadState
}
DownloadedTrack
interface DownloadedTrack {
trackId: string
originalTrack: TrackItem
localPath: string
localArtworkPath?: string | null
downloadedAt: number // Unix timestamp
fileSize: number // bytes
storageLocation: 'private' | 'public'
}
DownloadedPlaylist
interface DownloadedPlaylist {
playlistId: string
originalPlaylist: Playlist
downloadedTracks: DownloadedTrack[]
totalSize: number // bytes
downloadedAt: number // Unix timestamp
isComplete: boolean // All tracks downloaded
}
DownloadTask
interface DownloadTask {
downloadId: string
trackId: string
playlistId?: string | null
state: DownloadState
progress: DownloadProgress
createdAt: number
startedAt?: number | null
completedAt?: number | null
error?: DownloadError | null
retryCount: number
}
DownloadError
interface DownloadError {
code: string
message: string
reason: DownloadErrorReason
isRetryable: boolean
}
type DownloadErrorReason =
| 'network_error'
| 'storage_full'
| 'file_not_found'
| 'permission_denied'
| 'invalid_url'
| 'timeout'
| 'unknown'
Usage Examples
Download with Progress Tracking
import { DownloadManager, useDownloadProgress } from 'react-native-nitro-player'
function DownloadButton({ track }: { track: TrackItem }) {
const [downloadId, setDownloadId] = useState<string | null>(null)
const { getProgress } = useDownloadProgress({
trackIds: [track.id],
})
const handleDownload = async () => {
const id = await DownloadManager.downloadTrack(track)
setDownloadId(id)
}
const progress = getProgress(track.id)
return (
<View>
{!progress ? (
<Button title="Download" onPress={handleDownload} />
) : (
<View>
<Text>{Math.round(progress.progress * 100)}%</Text>
<Text>{progress.state}</Text>
</View>
)}
</View>
)
}
Offline Playback
import { DownloadManager, TrackPlayer } from 'react-native-nitro-player'
// Set preference to use downloaded tracks when available
DownloadManager.setPlaybackSourcePreference('auto')
// Play a track (will use local file if downloaded)
await TrackPlayer.playSong('track-id')
// Check what URL will be used
const track = { id: 'track-id', url: 'https://...' /* ... */ }
const effectiveUrl = await DownloadManager.getEffectiveUrl(track)
console.log('Playing from:', effectiveUrl) // Local path or network URL
Storage Management
import { DownloadManager } from 'react-native-nitro-player'
async function showStorageInfo() {
const info = await DownloadManager.getStorageInfo()
console.log(`Downloaded: ${info.trackCount} tracks`)
console.log(
`Total size: ${(info.totalDownloadedSize / 1024 / 1024).toFixed(2)} MB`
)
console.log(`Available: ${(info.availableSpace / 1024 / 1024).toFixed(2)} MB`)
}
// Clean up orphaned records
const cleaned = await DownloadManager.syncDownloads()
console.log(`Cleaned up ${cleaned} orphaned records`)
Download Queue Management
import { DownloadManager } from 'react-native-nitro-player'
// Get queue status
const status = DownloadManager.getQueueStatus()
console.log(`Active: ${status.activeCount}`)
console.log(`Pending: ${status.pendingCount}`)
console.log(`Overall: ${Math.round(status.overallProgress * 100)}%`)
// Pause all downloads
await DownloadManager.pauseAllDownloads()
// Resume all downloads
await DownloadManager.resumeAllDownloads()
// Cancel all downloads
await DownloadManager.cancelAllDownloads()
Best Practices
-
Configure on App Start: Configure the download manager when your app initializes.
-
Handle Errors: Always listen to
onDownloadStateChangeto handle download errors gracefully. -
WiFi-Only for Large Downloads: Enable
wifiOnlyDownloadsfor better user experience. -
Monitor Storage: Regularly check
getStorageInfo()to avoid running out of space. -
Sync Downloads: Call
syncDownloads()periodically to clean up orphaned records. -
Use Auto Playback Source: Set playback source to
'auto'to seamlessly use downloaded tracks when available. -
Background Downloads: Enable
backgroundDownloadsEnabledfor better user experience on Android.