Why Background Location Tracking Fails in React Native (And How I Fixed It with JSI & TurboModules)
About the Author
Hi, Iβm Punith Manthri, a Senior React Native Developer with 5 years of experience specializing in native module bridging, JSI, and the New Architecture. I am the creator and maintainer of react-native-pdf-jsi, a high-performance PDF library built on JSI that has surpassed 350,000+ downloads on npm (with over 65,000 weekly downloads). I spend most of my time writing native Kotlin, Swift, and C++ to push the boundaries of what React Native can do.
If you have ever built a React Native app that requires background location (like a delivery, fitness, or fleet-tracking app), you know the nightmare.
You write the code, test it in the simulator, and it works perfectly. You put the phone in your pocket, walk around the block, and look at your database. Half the data is missing.
Iβve used the major players (expo-location, Transistorsoft) and they are great, but I kept hitting the same production walls. So, I decided to build a native-only solution from scratch.
Here is what I learned about why background location breaks in React Native, and how the New Architecture (JSI/TurboModules) finally solves it.
1. JS Background Tasks are a Trap
If you rely on expo-task-manager or standard JS background tasks, your location logic runs in the JavaScript thread.
The Problem:
Modern mobile OSes (especially Android) are ruthless. When the device runs low on RAM, or sits idle for too long, the OS will kill your app process to save battery. When the process dies, the JS runtime dies. Your background task stops, and you only find out days later when your data has massive gaps.
The Fix:
The tracking engine must be 100% native.
- On Android, this means a Kotlin Foreground Service using
FusedLocationProvider. - On iOS, this means Swift
CLLocationManagerwith background modes enabled.
The JS thread should only be used to update the UI. The actual tracking and network requests must be detached from React entirely.
2. Motion Detection Needs to "Fail Open"
To save battery, most libraries use motion sensors (accelerometer, significant motion) to "park" the GPS when the device is stationary.
The Problem:
Android vendor ROMs (like Xiaomi or Samsung) and iOS CMMotionActivityManager frequently misclassify "slow movement" (like a hiker or a phone in a bag) as "stationary." The GPS silently sleeps, and you lose the route.
The Fix:
The motion gate must fail open. If the OS denies motion permissions, or the sensor fails to trigger, the engine must default to always-on GPS. It should never pause silently without a clear error state.
3. The "Duplicate Upload" Nightmare
If a user goes through a tunnel or a basement, they lose network. If you handle your retry logic in JavaScript, and the app is killed mid-request, you either lose the location points or duplicate them on your backend when the app restarts.
The Fix:
You need a native SQLite queue (using Room on Android).
- Every location fix is written to SQLite before anything else.
- Uploads are batched with Idempotency Keys.
- If a 5xx error or timeout occurs, the native layer uses exponential backoff with jitter.
By keeping the queue native, the sync survives process death. When the app reboots, the native layer picks up exactly where it left off.
4. The Apple & Google Store Rejection Problem
Background location is the #1 reason apps get rejected from the App Store and Google Play. Apple's "Prominent Disclosure" requirements and Google's "Foreground Service" declarations are a compliance nightmare.
The Fix:
You need precise Info.plist purpose strings, clear AndroidManifest.xml foreground service types, and a privacy manifest (PrivacyInfo.xcprivacy).
How the New Architecture (JSI) Changes the Game
Older background location libraries relied on the legacy bridge, which meant high latency and serialization overhead when sending high-frequency location points to JS.
By building exclusively for the New Architecture, I was able to use:
- TurboModules: To ensure synchronous, instant initialization of the native tracking service.
- JSI (JavaScript Interface): To query the native SQLite queue state directly from the JS thread without blocking or asynchronous overhead.
What I Built
Because I couldn't find an open-source solution that handled all of the above natively, I built React Native Background Location Pro.
It is a complete, production-grade SDK that runs Kotlin and Swift behind a TurboModule (JSI). It includes:
- π‘οΈ Self-healing tracking (survives app kills, reboots, and OS updates).
- πΆ Offline-first native SQLite queues.
- πͺ A "Store Approval Blueprint" to help you pass Apple/Google review.
- πΊοΈ A live map dashboard for testing.
Try it for free
I know commercial SDKs can be a hard sell, so I published a free debug evaluation build on npm. It runs the full production native engine (Kotlin/Swift binaries) with zero time limits in debug mode. You can test crash recovery, airplane mode, and the native queues in your own app before deciding if it's right for you.
View the source & docs:
π GitHub Repository
π NPM Package
Install the free eval:
bash
npm i react-native-background-location-pro-eval
Top comments (0)