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 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-bg-geolocation

v0.2.4

Published

Production-ready React Native background geolocation with iOS Location Push kill-state delivery, Android headless tracking, geofencing, motion detection, Socket.IO, and REST fallback.

Readme

react-native-bg-geolocation

Production-ready background geolocation for React Native apps across foreground, background, Android headless mode, and iOS Location Push Service Extension kill-state delivery.

React Native TypeScript iOS Android License: MIT

npm version npm downloads GitHub stars Buy Me A Coffee

react-native-bg-geolocation helps React Native apps collect and deliver reliable location updates with foreground tracking, background tracking, geofencing, motion detection, Socket.IO / REST delivery, Android headless JS, iOS Live Activity, and iOS force-quit location push support.

Foreground Tracking | Background Tracking | Kill-State Delivery | Geofencing | Motion Detection | Socket.IO + REST Fallback | iOS Location Push | Android Headless JS

Built with public platform APIs: FusedLocationProvider + Activity Recognition on Android, and CoreLocation + CoreMotion + BGTaskScheduler + CLLocationPushServiceExtension on iOS.

Features

  • ✅ Foreground / background location tracking with OS relaunch-capable events
  • ✅ Moving ↔ stationary detection (motion state machine + activity recognition)
  • ✅ OS-owned delivery (Android PendingIntent, iOS significant-change + regions)
  • iOS Location Push Service Extension — server-triggered location even when the app is force-quit (APNs location push → extension captures + uploads natively)
  • ✅ iOS Live Activity for Lock Screen and Dynamic Island tracking status
  • ✅ Headless JS task (Android) for kill-state JS execution
  • ✅ Pluggable delivery: native socket (Socket.IO) → REST fallback, or your own JS handler
  • ✅ Geofencing (CLCircularRegion / GeofencingClient)
  • ✅ Reboot persistence
  • ✅ Same API shape as react-native-background-geolocation (AuthorizationStatus, DesiredAccuracy, nested Config, etc.)

Installation

npm install react-native-bg-geolocation
# or
yarn add react-native-bg-geolocation

iOS

Add to Info.plist:

<key>NSLocationWhenInUseUsageDescription</key>
<string>Used to track your location.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Used to track your location in the background.</string>
<key>NSMotionUsageDescription</key>
<string>Used to detect movement.</string>
<key>UIBackgroundModes</key>
<array>
  <string>location</string>
  <!-- Required only when app.trackingAudioEnabled is approved and enabled. -->
  <string>audio</string>
  <string>fetch</string>
  <string>processing</string>
  <!-- Required for the app-alive background push path (Location Push). -->
  <string>remote-notification</string>
</array>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
  <string>com.bggeolocation.location-refresh</string>
</array>

For the Location Push Service Extension (kill-state), the app and extension also need the aps-environment and com.apple.developer.location.push entitlements and a shared App Group — see iOS Location Push Service Extension.

Register the BGTask in AppDelegate (see the example app's AppDelegate.swift).

iOS Live Activity

Live Activities require iOS 16.2 or newer and a WidgetKit extension in the host application. The example includes a complete extension in example/ios/BgGeolocationLiveActivity.

Add these keys to the app Info.plist:

<key>NSSupportsLiveActivities</key>
<true/>
<key>NSSupportsLiveActivitiesFrequentUpdates</key>
<true/>

Add ios/liveactivity/TSLiveTrackingAttributes.swift to the WidgetKit extension target and create an ActivityConfiguration for TSLiveTrackingAttributes. The package starts, updates, recovers, and ends the activity from native iOS location callbacks.

iOS Tracking Audio

For Apple-approved use cases, the package can run a real audible audio session beside Core Location while tracking is active:

app: {
  trackingAudioEnabled: true,
  trackingAudioVolume: 0.04,
  trackingAudioMixWithOthers: true,
}

Audio playback has no iOS runtime permission dialog. The host app must obtain clear user consent in its own UI before starting it. The app's Start and Stop actions control both tracking and audio. The package publishes the approved playback session through Now Playing, where Pause or Stop ends both playback and tracking. The tracking Live Activity remains a separate status surface. Add audio to UIBackgroundModes and enable this only for an audio use case and distribution model Apple has approved. Explicitly swiping the app away still terminates audio and location execution. Runtime status is available from (await BackgroundGeolocation.getState()).trackingAudio.

iOS Location Push Service Extension (kill-state tracking)

This is the production mechanism for getting a location from a force-quit app. Your server sends an APNs push to the device's location-push token; iOS launches a tiny CLLocationPushServiceExtension (a separate process, no React Native), which grabs one fix and uploads it — then the app is left untouched. This is how rideshare / delivery apps keep tracking after the user swipes the app away.

There is no JavaScript in the extension process. Delivery from a push is done natively by the SDK (Socket.IO → REST fallback), because on a kill-state / background wake the RN bridge is not running and JS event listeners would be dropped.

Two push types, two paths (your server may send either/both; the device uses whichever fires):

| App state | Push to send | Wakes | Who delivers | |---|---|---|---| | Alive (fg / backgrounded) | apns-push-type: background, content-available: 1 (standard APNs token) | the app | SDK natively, then fires the locationpush JS event for awareness | | Force-quit | apns-push-type: location to <bundle-id>.location-query (location-push token) | the extension | SDK natively inside the extension |

The push payload must carry a location-query id (any of location-query, locationQuery, location_query, locationQueryId, queryId). Its presence means "report a location now"; it is echoed back in the upload so the server can correlate.

1. Request the Apple entitlement (one-time, ~24–48h): https://developer.apple.com/contact/request/location-push-service-extension. Once approved, add com.apple.developer.location.push to the app entitlements.

2. Add an App Group shared by the app and the extension (e.g. group.<your-bundle-id>). The SDK writes its delivery config into this suite so the separate-process extension can read where/how to upload. Add the same identifier to both targets' Info.plist files:

<key>BGLocationPushAppGroupIdentifier</key>
<string>group.your.bundle.id</string>

3. Add a Location Push Service Extension target whose principal class is the SDK's BGLocationPushService. The example app ships a ready-made target and a script that wires it (example/ios/add_location_push_target.rb) — copy that target, or run the equivalent for your project. The extension's Info.plist:

<key>NSExtension</key>
<dict>
  <key>NSExtensionPointIdentifier</key>
  <string>com.apple.location.push.service</string>
  <key>NSExtensionPrincipalClass</key>
  <string>BGLocationPushService</string>
</dict>

4. Register the location-push token in AppDelegate and ship it to your server:

import CoreLocation
// keep a strong ref or the manager is deallocated before the callback
private var locationPushManager: CLLocationManager?

if #available(iOS 15.0, *) {
  let mgr = CLLocationManager()
  locationPushManager = mgr
  mgr.startMonitoringLocationPushes { tokenData, error in
    guard let data = tokenData else { return }
    let token = data.map { String(format: "%02hhx", $0) }.joined()
    UserDefaults.standard.set(token, forKey: "BGLocationManager_locationPushToken")
  }
}

Then in JS, after ready():

const token = await BackgroundGeolocation.getLocationPushToken(); // iOS only, else null
if (token) await myApi.registerLocationPushToken(token); // your endpoint

5. Configure delivery (where the extension uploads). Call once after ready():

await BackgroundGeolocation.setLocationPushConfig({
  socketUrl: 'https://your.server',     // Socket.IO base URL (tried first)
  socketPath: '/socket/location',
  socketEvent: 'location:update',
  socketAuthToken: jwt,                  // sent in the socket CONNECT auth + REST Bearer
  socketTimeout: 8,                      // seconds before falling back to REST
  fallbackUrl: 'https://your.server/api/location/fallback',
  fcmToken,                              // available as the {fcmToken} token
  // Custom HTTP headers for the REST fallback.
  headers: { 'Device-Type': 'ios' },
  // OPTIONAL: define the exact upload shape (socket emit + REST fallback).
  // Values may contain {tokens} the SDK substitutes with live values. Omit to
  // use the built-in default body.
  payloadTemplate: {
    lat: '{latitude}',                   // exact single token → stays numeric
    long: '{longitude}',
    fcm_token: '{fcmToken}',
    device_type: 'ios',                  // literal value, passed through
    user_current_time: '{userCurrentTime}',
    location_query_id: '{queryId}',
  },
});

payloadTemplate lets you ship any payload shape from JS — no native patching. Tokens (use inside string values): {latitude}/{lat}, {longitude}/{long}, {accuracy}, {speed}, {heading}, {altitude}, {timestamp}, {fcmToken}, {queryId}, {userCurrentTime} (local HH:mm), {deviceType}. A value that is exactly one token ('{latitude}') keeps its native type (numbers stay numbers); inline tokens ('v2-{queryId}') interpolate as strings; nested objects/arrays are supported. Without payloadTemplate the deliverer posts the default body { lat, long, fcm_token, device_type: "ios", user_current_time, location_query_id } (applies to both the socket emit and the REST fallback).

6. App-alive handler (optional). When the app is alive, the SDK still delivers natively and emits a locationpush event so your JS can react. The event carries delivered: true when native already sent it — do not re-send:

BackgroundGeolocation.onLocationPush(async (event) => {
  if (event.delivered) return; // native already uploaded it
  // fallback: deliver event.location yourself, then:
  await BackgroundGeolocation.finishLocationPush(event.requestId);
});

7. Server. Send apns-push-type: location to the location-push token with topic <bundle-id>.location-query (and/or a background push to the standard APNs token for the app-alive path). Requires "Always" location authorization.

Set app.locationPushEnabled: true in your config to signal this flow is in use.

Android

Permissions are declared by the library manifest (ACCESS_FINE_LOCATION, ACCESS_BACKGROUND_LOCATION, FOREGROUND_SERVICE_LOCATION, ACTIVITY_RECOGNITION, WAKE_LOCK, RECEIVE_BOOT_COMPLETED). Request the runtime permissions in JS (see the example).

Usage

import BackgroundGeolocation from 'react-native-bg-geolocation';

// 1. Configure (nested config, same shape as the original library)
await BackgroundGeolocation.ready({
  geolocation: {
    desiredAccuracy: BackgroundGeolocation.DesiredAccuracy.High,
    distanceFilter: 10,
    stopTimeout: 5,
    locationAuthorizationRequest: 'Always',
  },
  app: {
    stopOnTerminate: false,
    startOnBoot: true,
    enableHeadless: true,
    heartbeatInterval: 60,
    liveActivityEnabled: true,
    liveActivityTitle: 'Live location',
    liveActivitySubtitle: 'Background tracking is active',
    // Enable only after adding Push Notifications and ActivityKit APNs.
    liveActivityPushUpdates: false,
    trackingAudioEnabled: true,
    trackingAudioVolume: 0.04,
    trackingAudioMixWithOthers: true,
    foregroundService: true,
    notification: { title: 'Tracking', text: 'Location is active' },
  },
  logger: { logLevel: BackgroundGeolocation.LogLevel.Verbose },
});

// 2. Listen
const sub = BackgroundGeolocation.onLocation(location => {
  console.log('[location]', location.coords.latitude, location.coords.longitude);
});
BackgroundGeolocation.onMotionChange(event => {
  console.log('[motionchange]', event.isMoving ? 'moving' : 'stationary');
});

// 3. Request permission + start
const status = await BackgroundGeolocation.requestPermission();
if (status === BackgroundGeolocation.AuthorizationStatus.Always) {
  await BackgroundGeolocation.start();
}

// 4. Cleanup
sub.remove();
await BackgroundGeolocation.stop();

Headless task (Android kill state)

Register at module scope (e.g. in index.js) so it survives app kill:

import BackgroundGeolocation from 'react-native-bg-geolocation';

const headlessTask = async (event) => {
  if (event.name === 'location') {
    const { coords } = event.params;
    // POST to your server with fetch(), connect a socket, etc.
  }
};

BackgroundGeolocation.registerHeadlessTask(headlessTask);

How kill-state delivery works

| Platform | Mechanism | |---|---| | Android | A FusedLocation PendingIntent is owned by the OS and delivered to a BroadcastReceiver even when the process is dead. A START_STICKY foreground service keeps the process priority high; onTaskRemoved reschedules it via AlarmManager. Kill-state events fire the HeadlessJsTask, where your JS runs. | | iOS (OS relaunch) | Continuous background updates use Core Location. Significant-change and region events can relaunch an app the system terminated. Each native fix is persisted before upload; interrupted HTTP delivery retries on the next wake. After an explicit force-quit, iOS will not relaunch via Core Location until the app is reopened. | | iOS (force-quit, push-driven) | Your server sends an APNs location push (topic <bundle-id>.location-query) to the device's location-push token. iOS launches the Location Push Service Extension — a separate process, even when the app is force-quit — which captures one fix and uploads it natively (Socket.IO → REST fallback). See the Location Push section. Requires the Apple location.push entitlement, an App Group, and "Always" authorization. |

Live Activities do not collect location and do not provide unrestricted background execution. Their extension cannot access the network or receive location updates. Core Location performs tracking; ActivityKit presents the latest state. For updates while the app process is unavailable, read (await BackgroundGeolocation.getState()).liveActivity.pushToken, send it to your server, and update the activity through APNs using the <bundle-id>.push-type.liveactivity topic.

Devices without a Dynamic Island, including iPhone 12, show the Live Activity on the Lock Screen rather than persistently in the status bar.

API

  • Lifecycle: ready, start, stop, getState, setConfig, reset
  • Location: getCurrentPosition, watchPosition, stopWatchPosition, changePace, getOdometer, setOdometer, resetOdometer
  • Permissions: requestPermission, requestTemporaryFullAccuracy, getProviderState
  • Persistence: getLocations, getCount, destroyLocations, insertLocation, sync
  • Geofencing: addGeofence(s), removeGeofence(s), getGeofences, getGeofence, geofenceExists
  • Events: onLocation, onMotionChange, onActivityChange, onHeartbeat, onProviderChange, onGeofence, onEnabledChange, onHttp, onLocationPush, …
  • iOS Location Push (kill-state): getLocationPushToken, getApnsDeviceToken, setLocationPushConfig, onLocationPush, finishLocationPush — all iOS-only (resolve null / no-op on Android)
  • Enums: AuthorizationStatus, DesiredAccuracy, LogLevel, NotificationPriority, ActivityType, PersistMode, AccuracyAuthorization

Example

The example/ app demonstrates app-level tracking, socket delivery, motion detection and a live event log. See example/src/backgroundLocationService.ts for the recommended app-level integration pattern.

yarn
yarn example android   # or: yarn example ios

Support

If this package saves you time, please consider giving the repo a star. It helps other React Native developers discover the project.

Would you like to support me?

Follow on GitHub GitHub stars Buy Me A Coffee

Contributing

License

MIT © Zohaib Naseer and contributors.


Made with create-react-native-library