Background Geofencing in React Native (That Works When the App Is Closed)
GeofencingClient on Android) via a native module or SDK that registers regions with the OS and fires events without the JS runtime.
Almost every "geofencing in React Native" attempt fails the same way: it works while the app is open, then goes silent the moment the user switches away. The reason is fundamental to how RN works — and once you understand it, the right architecture is obvious.
Why JavaScript can't do it
React Native runs your JS on a runtime that the OS suspends when your app leaves the foreground. Background tasks, setInterval, and watchPosition all pause; when the app is killed, the JS engine is gone entirely. But geofencing's whole value is firing when the app isn't open. So anything built purely in JS is structurally incapable of background geofencing.
The approaches that don't hold up
- JS timers / background fetch — throttled and suspended; nothing runs when killed.
watchPositionfrom a JS geolocation lib — stops in the background; battery-heavy if you try to keep it alive.- Headless JS (Android) — helps for short tasks but isn't a substitute for OS region monitoring, and has no iOS equivalent.
The architecture that works
Push the geofencing down to native and let JS just configure and read results:
- A native module registers geofences with the OS (Core Location /
GeofencingClient). - The OS wakes native code on enter/exit — even when the app is closed.
- Native shows the local notification and records the event from cached content (no JS, no network needed at that instant).
- When JS next runs, it syncs the queued events up to your server.
You also inherit the platform quirks underneath: iOS's 20-region limit (register only the nearest zones and re-sync) and Android's habit of dropping geofences on reboot (re-register on BOOT_COMPLETED). A good RN geofencing layer handles both for you.
What the JS surface should look like
import { GeofenceSDK } from '@geofencekit/sdk';
// JS only configures + reads — the SDK does the native monitoring.
await GeofenceSDK.init({
baseUrl: 'https://api.geofencekit.com',
apiKey: 'sdk_...', // your key
userId: 'user_123',
});
const status = await GeofenceSDK.requestPermissions(); // asks for Always
if (status === 'granted_always') {
await GeofenceSDK.start(); // syncs nearest fences + registers with the OS
}
// enter/exit/dwell now fire natively — even with the app closed —
// and the local notification shows without a network round-trip.
ACCESS_BACKGROUND_LOCATION on Android 10+, and POST_NOTIFICATIONS on Android 13+. Ship a value-first priming screen before the OS dialog — "Always" is a big ask and the OS often won't let you re-prompt.
The short version
Background geofencing in React Native isn't a JS problem — it's a native one. Use a native module or SDK that registers regions with the OS, delivers events without the JS runtime, and handles the iOS region limit and Android re-registration underneath. Then your JS just calls start() and reads the results.
The native part, already done
GeofenceKit is one SDK for React Native (and Flutter, iOS, Android) over a shared native core: background enter/exit/dwell, offline queue, notifications from cached content, and the OS quirks handled. Install is npm install @geofencekit/sdk.