License sales launch: April 27, 2026 Get notified or view pricing

bglocation

Documentation

Everything you need to integrate background location tracking into your Capacitor app. The native core stays the same. The wrapper and setup flow adapt to your stack.

Quick Install

npm install @bglocation/capacitornpx cap sync

Keep capacitor.config.ts in sync with your native apps and rerun sync after changing plugin config or license settings.

Getting Started

Install the Capacitor wrapper, configure tracking, and get your first location update in under 5 minutes.

1. Install

npm install @bglocation/capacitor
npx cap sync

2. Configure & Start

import { BackgroundLocation } from '@bglocation/capacitor';

await BackgroundLocation.configure({
  distanceFilter: 15,
  desiredAccuracy: 'high',
  heartbeatInterval: 15,
  locationUpdateInterval: 5000,
  debug: true,
});

BackgroundLocation.addListener('onLocation', (location) => {
  console.log(location.latitude, location.longitude);
});

await BackgroundLocation.start();

3. Stop Tracking

await BackgroundLocation.stop();
await BackgroundLocation.removeAllListeners();

Permissions

On Android 10+, request foreground permission first, then background. On iOS, call requestPermissions() and let the system handle the Always vs When In Use flow.

React Native support targets 0.76+ with the New Architecture and TurboModules.

Configuration

All options available in configure(). The Capacitor wrapper mirrors the same native config surface and merges partial updates.

Core Options

OptionTypeDefaultDescription
distanceFilternumber | 'auto'15Minimum distance in meters between updates, or 'auto' for speed-adaptive mode.
desiredAccuracy'high' | 'balanced' | 'low''high'Accuracy preset used by the native location manager.
heartbeatIntervalnumber15Heartbeat interval in seconds. Fires even when stationary.
locationUpdateIntervalnumber5000Android-only update interval in milliseconds.
fastestLocationUpdateIntervalnumber2000Android-only lower bound for high-frequency updates.
debugbooleanfalseEnables verbose logs and onDebug events.

HTTP Posting

Native HTTP delivery works the same in Capacitor and React Native. The JavaScript wrapper configures it once, then URLSession on iOS and HttpURLConnection on Android post directly from the native layer.

await BackgroundLocation.configure({
  distanceFilter: 15,
  desiredAccuracy: 'high',
  heartbeatInterval: 15,
  locationUpdateInterval: 5000,
  fastestLocationUpdateInterval: 2000,
  http: {
    url: 'https://api.example.com/locations',
    headers: { Authorization: 'Bearer <token>' },
    buffer: { maxSize: 1000 },
  },
});

HTTP POST Body

{
  "location": {
    "latitude": 52.2297,
    "longitude": 21.0122,
    "accuracy": 5.0,
    "speed": 1.2,
    "heading": 180.0,
    "altitude": 110.5,
    "timestamp": 1700000000000,
    "isMoving": true,
    "isMock": false
  }
}

HttpEvent

interface HttpEvent {
  statusCode: number;
  success: boolean;
  responseText: string;
  error?: string;
  bufferedCount?: number;
}

Offline Buffer & Retry

Failed POSTs are stored in SQLite when buffer.maxSizeis configured. Buffered locations flush automatically in FIFO order after the next successful request.

Adaptive Distance Filter

Use auto mode to target stable update intervals regardless of whether the user is walking, cycling, or driving.

distanceFilter: 'auto',
autoDistanceFilter: {
  targetInterval: 10,
  minDistance: 10,
  maxDistance: 500,
}

Notification (Android)

Android tracking runs as a foreground service. Customize the persistent notification text from the wrapper.

notification: {
  title: 'Background Location',
  text: 'Tracking your route',
}

Partial Reconfiguration

Subsequent configure() calls merge with the last applied config. Pass only the fields you want to change.

API Reference

Complete TypeScript API exported by @bglocation/capacitor.

Methods

MethodReturnsDescription
getVersion()Promise<VersionInfo>Get wrapper and native core version information.
checkPermissions()Promise<LocationPermissionStatus>Check current foreground and background permission state.
requestPermissions(options?)Promise<LocationPermissionStatus>Request foreground or background location permissions.
configure(options)Promise<ConfigureResult>Configure tracking before start(); partial updates are merged.
start()Promise<LocationState>Start background location tracking.
stop()Promise<LocationState>Stop background location tracking.
getState()Promise<LocationState>Read the current enabled/tracking state.
getCurrentPosition(options?)Promise<Location>Request a single location update.
addGeofence(geofence)Promise<void>Register one geofence region.
addGeofences({ geofences })Promise<void>Register multiple geofences atomically.
removeGeofence({ identifier })Promise<void>Remove a geofence by identifier.
removeAllGeofences()Promise<void>Clear all geofences.
getGeofences()Promise<{ geofences: Geofence[] }>Return the currently registered geofences.
checkBatteryOptimization()Promise<BatteryWarningEvent>Inspect Android battery optimization state.
requestBatteryOptimization()Promise<BatteryWarningEvent>Open Android battery optimization settings.
removeAllListeners()Promise<void>Remove all active event listeners registered through the wrapper.

Events

Register listeners via BackgroundLocation.addListener(event, handler).

EventPayloadDescription
onLocationLocationLocation update based on distanceFilter.
onHeartbeatHeartbeatEventPeriodic heartbeat, even when stationary.
onProviderChangeProviderChangeEventGPS/network provider status changed.
onHttpHttpEventNative HTTP POST result.
onDebugDebugEventDebug log message when debug mode is enabled.
onBatteryWarningBatteryWarningEventBattery optimization warning on Android.
onAccuracyWarningAccuracyWarningEventApproximate location warning on iOS 14+.
onMockLocationMockLocationEventMock location detected on Android.
onPermissionRationalePermissionRationaleEventShow a custom rationale before background permission.
onTrialExpiredTrialExpiredEventTrial session ended and tracking stopped automatically.
onGeofenceGeofenceEventGeofence transition: enter, exit, or dwell.

Location Object

interface Location {
  latitude: number;
  longitude: number;
  accuracy: number;
  speed: number;
  heading: number;
  altitude: number;
  timestamp: number;
  isMoving: boolean;
  isMock: boolean;
  effectiveDistanceFilter?: number;
}

Geofencing

Geofencing is available through the same API surface in both wrappers. Up to 20 regions can be active at once.

interface Geofence {
  identifier: string;
  latitude: number;
  longitude: number;
  radius: number;
  notifyOnEntry?: boolean;
  notifyOnExit?: boolean;
  notifyOnDwell?: boolean;
  dwellDelay?: number;
  extras?: Record<string, string>;
}

GeofenceEvent Interface

interface GeofenceEvent {
  identifier: string;
  action: 'enter' | 'exit' | 'dwell';
  location: Location | null;
  extras?: Record<string, string>;
  timestamp: number;
}

Error Codes

CodeDescription
NOT_CONFIGUREDconfigure() must run before adding geofences.
GEOFENCE_LIMIT_EXCEEDEDThe wrapper enforces the 20 geofence limit.
GEOFENCE_ERRORNative registration failed.
UNSUPPORTEDThe current runtime does not support the attempted native operation.

Platform Guides

Platform-specific setup for iOS and Android. The native core is the same, but the wrapper decides how much gets automated for you.

iOS

Info.plist — Required Keys

Capacitor apps still need the usual location usage descriptions in the host app's Info.plist.

<key>NSLocationWhenInUseUsageDescription</key>
<string>We need your location to track your route.</string>

<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Background location is needed for continuous tracking.</string>

Background Modes

Enable Location updates in Xcode. Expo users get this automatically after expo prebuild when the config plugin is configured.

<key>UIBackgroundModes</key>
<array>
  <string>location</string>
</array>

SLC Fallback

Significant Location Change monitoring is registered automatically. If iOS kills the app, the native core uses SLC to relaunch and resume standard GPS tracking.

Minimum Deployment Target

iOS 15.0+.

Android

Permissions

The Capacitor plugin contributes its manifest entries automatically during sync and manifest merge.

ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
ACCESS_BACKGROUND_LOCATION
FOREGROUND_SERVICE
FOREGROUND_SERVICE_LOCATION
POST_NOTIFICATIONS

Foreground Service

Tracking runs inside a foreground service with a persistent notification. This is what keeps Android from killing the process during long sessions.

Battery Optimization

OEM battery killers are detected automatically. The wrapper emits onBatteryWarning with an OEM-specific help URL.

Two-Step Permission Flow

await BackgroundLocation.requestPermissions({
  permissions: ['location'],
});

await BackgroundLocation.requestPermissions({
  permissions: ['backgroundLocation'],
});

Minimum SDK

minSdk 26 (Android 8.0), compileSdk 36.

Licensing

Trial mode, offline validation, bundle ID binding, and where to place your license key.

Trial Mode

  • 30-minute sessions with automatic stop and an onTrialExpired event.
  • 1-hour cooldown between trial sessions.
  • Debug mode is forced on so you can inspect behavior before buying.

Adding a License Key

Place the key in capacitor.config.ts and rerun sync.

// capacitor.config.ts
import type { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  appId: 'com.yourcompany.app',
  plugins: {
    BackgroundLocation: {
      licenseKey: 'BGL1-eyJ...',
    },
  },
};

export default config;

How Validation Works

  • RSA-2048 signed payloads are verified fully offline on-device.
  • The license is bound to a bundle ID and can be used across the Capacitor and React Native wrappers for the same app identifier.
  • The license itself is perpetual. One year of updates is included, then update access can be renewed.

License Status

const result = await BackgroundLocation.configure({ ... });

// result.licenseMode: 'full' | 'trial'
// result.licenseUpdatesUntil?: string
// result.licenseUpdateExpired?: boolean
// result.licenseError?: string

Examples

Real-world integration patterns for common production use cases.

Fleet / Delivery Tracking

Continuous tracking with native HTTP posting and automatic offline buffering.

import { BackgroundLocation } from '@bglocation/capacitor';

await BackgroundLocation.configure({
  distanceFilter: 'auto',
  autoDistanceFilter: {
    targetInterval: 10,
    minDistance: 10,
    maxDistance: 500,
  },
  desiredAccuracy: 'high',
  heartbeatInterval: 30,
  http: {
    url: 'https://api.fleet.com/vehicle/location',
    headers: { Authorization: 'Bearer <token>' },
    buffer: { maxSize: 2000 },
  },
});

BackgroundLocation.addListener('onHttp', (event) => {
  if (!event.success) {
    console.warn('Buffered:', event.bufferedCount);
  }
});

await BackgroundLocation.start();

Fitness / Running App

High-frequency tracking with in-memory processing for pace, route, and progress stats.

import { BackgroundLocation } from '@bglocation/capacitor';
import type { Location } from '@bglocation/capacitor';

const route: Location[] = [];

await BackgroundLocation.configure({
  distanceFilter: 5,
  desiredAccuracy: 'high',
  heartbeatInterval: 10,
  locationUpdateInterval: 3000,
});

BackgroundLocation.addListener('onLocation', (location) => {
  route.push(location);
  updateMapPolyline(route);
});

await BackgroundLocation.start();

Geofencing — Points of Interest

Register circular regions and react to enter, exit, and dwell transitions.

import { BackgroundLocation } from '@bglocation/capacitor';

await BackgroundLocation.configure({
  distanceFilter: 50,
  desiredAccuracy: 'balanced',
  heartbeatInterval: 60,
});

await BackgroundLocation.addGeofence({
  identifier: 'office',
  latitude: 52.2297,
  longitude: 21.0122,
  radius: 100,
  notifyOnEntry: true,
  notifyOnExit: true,
});

BackgroundLocation.addListener('onGeofence', (event) => {
  console.log(event.identifier, event.action);
});

await BackgroundLocation.start();