I ran into this while building Orb, a private offline AI app for Android.
Orb runs local models on the phone. That is great for privacy, but it creates a very practical mobile problem: the app has to download model files that can be multiple gigabytes. If the network drops, the app backgrounds, or the user force-closes the app, restarting from zero is a terrible experience.
So I pulled the downloader out into a small open-source package:
GitHub: https://github.com/zraisan/react-native-client
NPM: https://www.npmjs.com/package/react-native-client
react-native-client is a native HTTP client for React Native apps that need direct-to-file downloads without moving bytes through the JavaScript bridge. The first stable API is focused on downloads, not general HTTP requests.
What it does
The package exposes a typed downloadFile API through Nitro Modules.
Under the hood:
- Android uses OkHttp
- iOS uses URLSession
- files are streamed directly to disk on the native side
- progress callbacks report bytes written and total length
- resumable downloads use HTTP Range requests
- partial-file resume validates
Content-Rangebefore appending - Android background mode uses a foreground service
- iOS uses a background URLSession path
The main use case is large files: AI models, media packs, offline datasets, cache warmups, and anything else where a failed 2GB download should not mean starting over.
Basic usage
import { downloadFile, documentDirectoryPath } from 'react-native-client'
const result = await downloadFile({
fromUrl: 'https://example.com/model.gguf',
toFile: `${documentDirectoryPath}/model.gguf`,
})
console.log(result.statusCode, result.bytesWritten)
With progress:
await downloadFile({
fromUrl: 'https://example.com/large-file.bin',
toFile: `${documentDirectoryPath}/large-file.bin`,
begin: (statusCode, contentLength) => {
console.log('started', statusCode, contentLength)
},
onProgress: (bytesWritten, contentLength) => {
if (contentLength > 0) {
console.log(`${Math.round((bytesWritten / contentLength) * 100)}%`)
}
},
})
Resumable background download:
await downloadFile({
fromUrl: 'https://example.com/model.gguf',
toFile: `${documentDirectoryPath}/model.gguf`,
resumable: true,
background: true,
connectionTimeout: 30000,
readTimeout: 30000,
onProgress: (bytesWritten, contentLength) => {
console.log(bytesWritten, contentLength)
},
})
To resume after app relaunch, call the same request again with the same toFile and resumable: true. If the partial file is still there, the native client requests only the missing range.
const modelPath = `${documentDirectoryPath}/model.gguf`
await downloadFile({
fromUrl: modelUrl,
toFile: modelPath,
resumable: true,
background: true,
})
Why not just fetch?
For small files, normal app-level download logic is fine.
For Orb, the files are not small. A local AI model download needs to survive boring real-world mobile behavior: bad Wi-Fi, app backgrounding, retries, foreground service expectations on Android, and users reopening the app later.
I wanted the heavy transfer to stay native:
- no file bytes crossing the JS bridge
- native networking behavior on each platform
- progress events for the UI
- partial-file recovery when the server supports Range
- safe restart when resume metadata is wrong or unsupported
The package is not trying to be a full download manager yet. It does not currently expose task IDs, cancellation, a persistent queue, checksums, uploads, or a general request/response client. The current goal is narrower: make large direct-to-file downloads reliable enough to build real product flows around them.
Install
npm install react-native-client react-native-nitro-modules
yarn add react-native-client react-native-nitro-modules
bun add react-native-client react-native-nitro-modules
For iOS:
cd ios
pod install
Android should work through normal React Native autolinking.
Current status
The package is early, but usable for the download flow it was built for.
Current version: 0.0.3
Requirements:
- React Native app
react-native-nitro-modules >=0.35.0 <0.36.0- Android and/or iOS native builds
The README has a demo showing Android resume behavior: start a native background download, send the app home, return to the transfer, force-stop the app, relaunch, and resume from the partial file.
Why Iām releasing it
Orb needed this because private offline AI has a surprisingly unglamorous bottleneck: getting the model onto the device cleanly. The AI part gets the attention, but the first user experience is often just a large download that must not fail in a stupid way.
If you are building a React Native app that downloads big files, Iād love feedback on the API before it grows too much.
The next useful pieces are probably:
- task IDs and cancellation
- persistent background task registry
- request headers and response metadata
- checksum verification
- upload support
- broader HTTP APIs beyond file download
GitHub: https://github.com/zraisan/react-native-client
NPM: https://www.npmjs.com/package/react-native-client
Orb on Google Play: https://play.google.com/store/apps/details?id=com.falaq.orb
Top comments (0)