GeofenceKit Start free
React Native · Location

Background Geofencing in React Native (That Works When the App Is Closed)

24 July 2026 · ~6 min read
TL;DR: Your JavaScript stops running the moment a React Native app is backgrounded or killed — so geofencing cannot live in JS. It has to run in native code (Core Location on iOS, 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

The architecture that works

Push the geofencing down to native and let JS just configure and read results:

  1. A native module registers geofences with the OS (Core Location / GeofencingClient).
  2. The OS wakes native code on enter/exit — even when the app is closed.
  3. Native shows the local notification and records the event from cached content (no JS, no network needed at that instant).
  4. 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.
Permissions to get right: background geofencing needs Always location (a two-step prompt on iOS), 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.

Start free — get an API key