npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@kesha-antonov/react-native-background-downloader

v4.3.2

Published

A library for React-Native to help you download large files on iOS and Android both in the foreground and most importantly in the background.

Downloads

10,736

Readme

npm version

🎉 Version 4.0.0 Released!

v4.0.0 is now available with full React Native New Architecture (TurboModules) support!

What's New

  • ✅ Full TurboModules support for iOS and Android
  • ✅ Expo Config Plugin for automatic iOS setup
  • ✅ Android code converted to Kotlin
  • ✅ Full TypeScript support
  • ✅ New progressMinBytes option for hybrid progress reporting
  • maxRedirects option for Android

Upgrading from v3.x?

📖 See the Migration Guide for detailed upgrade instructions and breaking changes.

📋 See the Changelog for the full list of changes.

Looking for v3.2.6?

If you need the previous stable version: 3.2.6 readme

@kesha-antonov/react-native-background-downloader

A library for React-Native to help you download large files on iOS and Android both in the foreground and most importantly in the background.

Why?

On iOS, if you want to download big files no matter the state of your app, wether it's in the background or terminated by the OS, you have to use a system API called NSURLSession.

This API handles your downloads separately from your app and only keeps it informed using delegates (Read: Downloading Files in the Background).

On Android we are using similar process with DownloadManager

The real challenge of using this method is making sure the app's UI is always up-to-date with the downloads that are happening in another process because your app might startup from scratch while the downloads are still running.

@kesha-antonov/react-native-background-downloader gives you an easy API to both downloading large files and re-attaching to those downloads once your app launches again.

ToC

Getting started

Installation

yarn add @kesha-antonov/react-native-background-downloader

or

npm i @kesha-antonov/react-native-background-downloader

Then:

cd ios && pod install

If you need to manually configure the package for New Architecture:

iOS: The library automatically detects New Architecture via compile-time flags.

Android: For New Architecture, you can optionally use RNBackgroundDownloaderTurboPackage instead of the default package:

import com.eko.RNBackgroundDownloaderTurboPackage;

// In your MainApplication.java
@Override
protected List<ReactPackage> getPackages() {
  return Arrays.<ReactPackage>asList(
    // ... other packages
    new RNBackgroundDownloaderTurboPackage() // For New Architecture
  );
}

Mostly automatic installation

Any React Native version >= 0.60 supports autolinking so nothing should be done.

For anything < 0.60 run the following link command

$ react-native link @kesha-antonov/react-native-background-downloader

Manual installation

iOS

  1. In XCode, in the project navigator, right click LibrariesAdd Files to [your project's name]
  2. Go to node_modules@kesha-antonov/react-native-background-downloader and add RNBackgroundDownloader.xcodeproj
  3. In XCode, in the project navigator, select your project. Add libRNBackgroundDownloader.a to your project's Build PhasesLink Binary With Libraries
  4. Run your project (Cmd+R)

Android

  1. Open up android/app/src/main/java/[...]/MainActivity.java
  • Add import com.eko.RNBackgroundDownloaderPackage; to the imports at the top of the file
  • Add new RNBackgroundDownloaderPackage() to the list returned by the getPackages() method
  1. Append the following lines to android/settings.gradle:
    include ':react-native-background-downloader'
    project(':react-native-background-downloader').projectDir = new File(rootProject.projectDir,   '../node_modules/@kesha-antonov/react-native-background-downloader/android')
  2. Insert the following lines inside the dependencies block in android/app/build.gradle:
      compile project(':react-native-background-downloader')

iOS - Extra Mandatory Step

Option 1: Using Expo Config Plugin (Recommended for Expo/EAS users)

If you're using Expo or EAS Build, you can use the included Expo config plugin to automatically configure the native code:

In your app.json:

{
  "expo": {
    "name": "Your App",
    "plugins": [
      "@kesha-antonov/react-native-background-downloader"
    ]
  }
}

Or in your app.config.js:

export default {
  expo: {
    name: "Your App",
    plugins: [
      "@kesha-antonov/react-native-background-downloader"
    ]
  }
}

Plugin Options:

You can customize the plugin behavior with options:

// app.config.js
export default {
  expo: {
    name: "Your App",
    plugins: [
      ["@kesha-antonov/react-native-background-downloader", {
        // Set to false if you're already using react-native-mmkv
        addMmkvDependency: true,
        // Customize the MMKV version (default: '2.2.4')
        mmkvVersion: "2.2.4"
      }]
    ]
  }
}

| Option | Type | Default | Description | |--------|------|---------|-------------| | addMmkvDependency | boolean | true | Whether to automatically add MMKV dependency on Android. Set to false if you're using react-native-mmkv. | | mmkvVersion | string | '2.2.4' | The version of MMKV to use on Android. |

The plugin will automatically:

  • iOS: Add the required import to your AppDelegate (Objective-C) or Bridging Header (Swift)
  • iOS: Add the handleEventsForBackgroundURLSession method to your AppDelegate
  • iOS: Handle both React Native < 0.77 (Objective-C) and >= 0.77 (Swift) projects
  • Android: Add the required MMKV dependency (unless addMmkvDependency: false)

After adding the plugin, run:

expo prebuild --clean

Option 2: Manual Setup

In your project bridging header file (e.g. ios/{projectName}-Bridging-Header.h) add an import for RNBackgroundDownloader:

...
#import <RNBackgroundDownloader.h>

Then in your AppDelegate.swift add the following method inside of your AppDelegate class:

...

@main
class AppDelegate: UIResponder, UIApplicationDelegate
  ...

  func application(
    _ application: UIApplication,
    handleEventsForBackgroundURLSession identifier: String,
    completionHandler: @escaping () -> Void
  ) {
    RNBackgroundDownloader.setCompletionHandlerWithIdentifier(identifier, completionHandler: completionHandler)
  }
}
...

Failing to add this code will result in canceled background downloads. If Xcode complains that RNBackgroundDownloader.h is missing, you might have forgotten to pod install first.

In your AppDelegate.m add the following code:

...
#import <RNBackgroundDownloader.h>

...

- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler
{
  [RNBackgroundDownloader setCompletionHandlerWithIdentifier:identifier completionHandler:completionHandler];
}

...

Failing to add this code will result in canceled background downloads. If Xcode complains that RNBackgroundDownloader.h is missing, you might have forgotten to pod install first.

Usage

Downloading a file

import { Platform } from 'react-native'
import { createDownloadTask, completeHandler, directories } from '@kesha-antonov/react-native-background-downloader'

const jobId = 'file123'

let task = createDownloadTask({
  id: jobId,
  url: 'https://link-to-very.large/file.zip',
  destination: `${directories.documents}/file.zip`,
  metadata: {}
}).begin(({ expectedBytes, headers }) => {
  console.log(`Going to download ${expectedBytes} bytes!`)
}).progress(({ bytesDownloaded, bytesTotal }) => {
  console.log(`Downloaded: ${bytesDownloaded / bytesTotal * 100}%`)
}).done(({ bytesDownloaded, bytesTotal }) => {
  console.log('Download is done!', { bytesDownloaded, bytesTotal })

  // PROCESS YOUR STUFF

  // FINISH DOWNLOAD JOB
  completeHandler(jobId)
}).error(({ error, errorCode }) => {
  console.log('Download canceled due to error: ', { error, errorCode });
})

// starts download
task.start()

// ...later

// Pause the task
await task.pause()

// Resume after pause
await task.resume()

// Cancel the task
await task.stop()

Re-Attaching to background downloads

This is the main selling point of this library (but it's free!).

What happens to your downloads after the OS stopped your app? Well, they are still running, we just need to re-attach to them.

Add this code to app's init stage, and you'll never lose a download again!

import { getExistingDownloadTasks } from '@kesha-antonov/react-native-background-downloader'

let lostTasks = await getExistingDownloadTasks()
for (let task of lostTasks) {
  console.log(`Task ${task.id} was found!`)
  task.progress(({ bytesDownloaded, bytesTotal }) => {
    console.log(`Downloaded: ${bytesDownloaded / bytesTotal * 100}%`)
  }).done(({ location, bytesDownloaded, bytesTotal }) => {
    console.log('Download is done!', { location, bytesDownloaded, bytesTotal })
  }).error(({ error, errorCode }) => {
    console.log('Download canceled due to error: ', { error, errorCode })
  })
}

task.id is very important for re-attaching the download task with any UI component representing that task. This is why you need to make sure to give sensible IDs that you know what to do with, try to avoid using random IDs.

Using custom headers

If you need to send custom headers with your download request, you can do in it 2 ways:

  1. Globally using setConfig():
import { setConfig } from '@kesha-antonov/react-native-background-downloader'

setConfig({
  headers: {
    Authorization: 'Bearer 2we$@$@Ddd223',
  }
})

This way, all downloads with have the given headers.

  1. Per download by passing a headers object in the options of createDownloadTask():
import { createDownloadTask, directories } from '@kesha-antonov/react-native-background-downloader'

const task = createDownloadTask({
  id: 'file123',
  url: 'https://link-to-very.large/file.zip'
  destination: `${directories.documents}/file.zip`,
  headers: {
    Authorization: 'Bearer 2we$@$@Ddd223'
  }
}).begin(({ expectedBytes, headers }) => {
  console.log(`Going to download ${expectedBytes} bytes!`)
}).progress(({ bytesDownloaded, bytesTotal }) => {
  console.log(`Downloaded: ${bytesDownloaded / bytesTotal * 100}%`)
}).done(({ location, bytesDownloaded, bytesTotal }) => {
  console.log('Download is done!', { location, bytesDownloaded, bytesTotal })
}).error(({ error, errorCode }) => {
  console.log('Download canceled due to error: ', { error, errorCode })
})

task.start()

Headers given in createDownloadTask() are merged with the ones given in setConfig({ headers: { ... } }).

Handling Slow-Responding URLs

This library automatically includes connection timeout improvements for slow-responding URLs. By default, the following headers are added to all download requests on Android:

  • Connection: keep-alive - Keeps the connection open for better handling
  • Keep-Alive: timeout=600, max=1000 - Sets a 10-minute keep-alive timeout
  • User-Agent: ReactNative-BackgroundDownloader/3.2.6 - Proper user agent for better server compatibility

These headers help prevent downloads from getting stuck in "pending" state when servers take several minutes to respond initially. You can override these headers by providing your own in the headers option.

Handling URLs with Many Redirects (Android)

Android's DownloadManager has a built-in redirect limit that can cause ERROR_TOO_MANY_REDIRECTS for URLs with multiple redirects (common with podcast URLs, tracking services, CDNs, etc.).

To handle this, you can use the maxRedirects option to pre-resolve redirects before passing the final URL to DownloadManager:

import { Platform } from 'react-native'
import { createDownloadTask, directories } from '@kesha-antonov/react-native-background-downloader'

// Example: Podcast URL with multiple redirects
const task = createDownloadTask({
  id: 'podcast-episode',
  url: 'https://pdst.fm/e/chrt.fm/track/479722/arttrk.com/p/example.mp3',
  destination: `${directories.documents}/episode.mp3`,
  maxRedirects: 10, // Follow up to 10 redirects before downloading
}).begin(({ expectedBytes }) => {
  console.log(`Going to download ${expectedBytes} bytes!`)
}).progress(({ bytesDownloaded, bytesTotal }) => {
  console.log(`Downloaded: ${bytesDownloaded / bytesTotal * 100}%`)
}).done(({ location, bytesDownloaded, bytesTotal }) => {
  console.log('Download is done!', { location, bytesDownloaded, bytesTotal })
}).error(({ error, errorCode }) => {
  console.log('Download canceled due to error: ', { error, errorCode })

  if (errorCode === 1005) { // ERROR_TOO_MANY_REDIRECTS
    console.log('Consider increasing maxRedirects or using a different URL')
  }
})

task.start()

Notes on maxRedirects:

  • Only available on Android (iOS handles redirects automatically)
  • If not specified or set to 0, no redirect resolution is performed
  • Uses HEAD requests to resolve redirects efficiently
  • Falls back to original URL if redirect resolution fails
  • Respects the same headers and timeouts as the main download

API

Named Exports

The library exports the following functions and objects:

import {
  setConfig,
  createDownloadTask,
  getExistingDownloadTasks,
  completeHandler,
  directories
} from '@kesha-antonov/react-native-background-downloader'

createDownloadTask(options)

Download a file to destination

options

An object containing options properties

| Property | Type | Required | Platforms | Info | | ------------- | ------------------------------------------------ | :------: | :-------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | id | String | ✅ | All | A unique ID to provide for this download. This ID will help to identify the download task when the app re-launches | | url | String | ✅ | All | URL to file you want to download | | destination | String | ✅ | All | Where to copy the file to once the download is done. The 'file://' prefix will be automatically removed if present | | metadata | Record<string, unknown> | | All | Custom data to be preserved across app restarts. Will be serialized to JSON | | headers | Record<string, string | null> | | All | Custom headers to add to the download request. These are merged with the headers given in setConfig({ headers: { ... } }). Headers with null values will be removed | | maxRedirects | Number | | Android | Maximum number of redirects to follow before passing URL to DownloadManager. If not specified or 0, no redirect resolution is performed. Helps avoid ERROR_TOO_MANY_REDIRECTS for URLs with many redirects (e.g., podcast URLs) | | isAllowedOverRoaming | Boolean | | Android | whether this download may proceed over a roaming connection. By default, roaming is allowed | | isAllowedOverMetered | Boolean | | Android | Whether this download may proceed over a metered network connection. By default, metered networks are allowed | | isNotificationVisible | Boolean | | Android | Whether to show a download notification or not | | notificationTitle | String | | Android | Title of the download notification |

returns

DownloadTask - The download task to control and monitor this download. Call task.start() to begin the download.

getExistingDownloadTasks()

Checks for downloads that ran in background while your app was terminated.

Recommended to run at the init stage of the app.

returns

Promise<DownloadTask[]> - A promise that resolves to an array of tasks that were running in the background so you can re-attach callbacks to them

setConfig(config)

Sets global configuration for the downloader.

config

An object containing configuration properties

| Name | Type | Info | | -------------- | ------ | ---------------------------------------------------------------------------------------------------- | | headers | Record<string, string | null> | Optional headers to use in all future downloads. Headers with null values will be removed | | progressInterval | Number | Interval in milliseconds for download progress updates. Must be >= 250. Default is 1000 | | progressMinBytes | Number | Minimum number of bytes that must be downloaded before a progress event is emitted. When set to 0, only the percentage threshold (1% change) triggers progress updates. Default is 1048576 (1MB) | | isLogsEnabled | Boolean | Enables/disables debug logs in library. Default is false |

DownloadTask

A class representing a download task created by createDownloadTask(). Note: You must call task.start() to begin the download after setting up event handlers.

Members

| Name | Type | Info | | -------------- | ------ | ---------------------------------------------------------------------------------------------------- | | id | String | The id you gave the task when calling createDownloadTask | | metadata | Record<string, unknown> | The metadata you gave the task when calling createDownloadTask | | state | 'PENDING' | 'DOWNLOADING' | 'PAUSED' | 'DONE' | 'FAILED' | 'STOPPED' | Current state of the download task | | bytesDownloaded | Number | The number of bytes currently written by the task | | bytesTotal | Number | The number bytes expected to be written by this task or more plainly, the file size being downloaded. Note: This value will be -1 if the server does not provide a Content-Length header | | downloadParams | DownloadParams | The download parameters set for this task |

completeHandler(jobId: string)

Finishes download job and informs OS that app can be closed in background if needed. After finishing download in background you have some time to process your JS logic and finish the job.

Parameters:

  • jobId (String) - The ID of the download task to complete

Note: This should be called after processing your download in the done callback to properly signal completion to the OS.

Callback Methods

Use these methods to stay updated on what's happening with the task.

All callback methods return the current instance of the DownloadTask for chaining.

| Function | Callback Arguments | Info| | ---------- | --------------------------------- | ---- | | begin | { expectedBytes: number, headers: Record<string, string \| null> } | Called when the first byte is received. 💡: this is good place to check if the device has enough storage space for this download | | progress | { bytesDownloaded: number, bytesTotal: number } | Called based on progressInterval (default: every 1000ms) so you can update your progress bar accordingly. Note: bytesTotal will be -1 if the server does not provide a Content-Length header | | done | { location: string, bytesDownloaded: number, bytesTotal: number } | Called when the download is done, the file is at the destination you've set. location is the final file path. Note: bytesTotal will be -1 if the server did not provide a Content-Length header | | error | { error: string, errorCode: number } | Called when the download stops due to an error |

pause(): Promise<void>

Pauses the download. Returns a promise that resolves when the pause operation is complete.

Note: On Android, pause/resume is implemented using HTTP Range headers, which requires server support. The download progress is saved and resumed from where it left off.

resume(): Promise<void>

Resumes a paused download. Returns a promise that resolves when the resume operation is complete.

Note: On Android, this uses HTTP Range headers to resume from the last downloaded byte position. If the server doesn't support range requests, the download will restart from the beginning.

stop(): Promise<void>

Stops the download for good and removes the file that was written so far. Returns a promise that resolves when the stop operation is complete.

Constants

directories

documents

An absolute path to the app's documents directory. It is recommended that you use this path as the target of downloaded files.

Platform-Specific Limitations

Android MMKV Dependency

This library uses MMKV for persistent storage of download state on Android. The MMKV dependency is declared as compileOnly, meaning your app must provide it.

If you're using react-native-mmkv: No additional setup needed - react-native-mmkv already provides the required MMKV dependency.

If you're NOT using react-native-mmkv: Add the MMKV dependency to your app's android/app/build.gradle:

dependencies {
    // ... other dependencies
    implementation 'com.tencent:mmkv-shared:2.2.4'  // or newer
}

Note: MMKV 2.0.0+ is required for Android 15+ support (16KB memory page sizes).

Android DownloadManager Limitations

The Android implementation uses the system's DownloadManager service for downloads, with custom pause/resume support:

Pause/Resume Support

  • Implementation: Pause/resume on Android is implemented using HTTP Range headers
  • How it works: When you pause a download, the current progress is saved. When resumed, a new download starts from where it left off using the Range header
  • Server requirement: The server must support HTTP Range requests for resume to work correctly. If the server doesn't support range requests, the download will restart from the beginning
  • Temp files: During pause/resume, progress is stored in a .tmp file which is renamed to the final destination upon completion

Rules for proguard-rules.pro

If you encounter java.lang.IllegalStateException: TypeToken must be created with a type argument: new TypeToken<...>() in Android release builds, add these rules to your proguard-rules.pro:

# react-native-background-downloader - Keep config class used by Gson
-keep class com.eko.RNBGDTaskConfig { *; }

# Gson TypeToken support
-keepattributes Signature
-keep class com.google.gson.reflect.TypeToken { *; }
-keep class * extends com.google.gson.reflect.TypeToken

# MMKV
-keep class com.tencent.mmkv.** { *; }
-dontwarn com.tencent.mmkv.**

Known Issues with New Architecture

When using larger files with the New Architecture, you may encounter ERROR_CANNOT_RESUME (error code 1008). This is a known limitation of Android's DownloadManager, not specific to this library or the New Architecture. The error includes enhanced messaging to help diagnose the issue.

Workaround: If you encounter this error frequently with large files, consider:

  1. Breaking large downloads into smaller chunks
  2. Implementing retry logic in your app
  3. Using alternative download strategies for very large files

The library now provides enhanced error handling for this specific case with detailed logging and cleanup.

TODO

  • [ ] Write better API for downloads - current kinda boilerplate

Authors

Re-written & maintained by Kesha Antonov

Originally developed by Elad Gil of Eko

License

Apache 2