Skip to main content

Examples

Copy-paste patterns for the most common integrations. Every example uses only the public API.

A reusable hook​

Wrap the module once and use it across your app:

useLocationTracker.ts
import {useCallback, useEffect, useState} from 'react';
import LocationTracker, {
type LocationEvent,
type Status,
type SyncEvent,
} from 'react-native-background-location-pro-eval';

export function useLocationTracker() {
const [status, setStatus] = useState<Status>(() => LocationTracker.getStatus());
const [lastPoint, setLastPoint] = useState<LocationEvent | null>(null);
const [sync, setSync] = useState<SyncEvent | null>(null);

useEffect(() => {
const refresh = () => setStatus(LocationTracker.getStatus());

const locationSub = LocationTracker.onLocationUpdate(point => {
setLastPoint(point);
refresh();
});
const syncSub = LocationTracker.onSyncStatus(event => {
setSync(event);
refresh();
});

return () => {
locationSub.remove();
syncSub.remove();
};
}, []);

const start = useCallback((serverUrl: string) => {
LocationTracker.startTracking(serverUrl);
setStatus(LocationTracker.getStatus());
}, []);

const stop = useCallback(() => {
LocationTracker.stopTracking();
setStatus(LocationTracker.getStatus());
}, []);

return {status, lastPoint, sync, start, stop};
}

Start and stop tracking with a permission check​

TrackingButton.tsx
import React from 'react';
import {Alert, Button} from 'react-native';
import LocationTracker from 'react-native-background-location-pro-eval';
import {useLocationTracker} from './useLocationTracker';

const SERVER_URL = 'https://api.example.com';

export function TrackingButton() {
const {status, start, stop} = useLocationTracker();

const onStart = () => {
const {authorization, buildTier} = LocationTracker.getStatus();

if (buildTier === 'release') {
Alert.alert('Evaluation build', 'Tracking is available in debug builds only.');
return;
}
if (authorization !== 'always') {
Alert.alert(
'Allow background location',
'To record your trip while the app is closed, choose "Allow all the time" / "Always".',
[
{text: 'Not now', style: 'cancel'},
{text: 'Continue', onPress: () => LocationTracker.requestLocationPermission(true)},
],
);
return;
}
start(SERVER_URL);
};

return status.desired ? (
<Button title="Stop tracking" onPress={stop} />
) : (
<Button title="Start tracking" onPress={onStart} />
);
}

Show the live route on a map​

Using react-native-maps:

LiveRoute.tsx
import React, {useEffect, useState} from 'react';
import MapView, {Polyline} from 'react-native-maps';
import LocationTracker from 'react-native-background-location-pro-eval';

type LatLng = {latitude: number; longitude: number};

export function LiveRoute() {
const [path, setPath] = useState<LatLng[]>([]);

useEffect(() => {
const sub = LocationTracker.onLocationUpdate(p => {
if (p.accuracy > 50) return; // skip low-accuracy fixes in the UI
setPath(prev => [...prev, {latitude: p.latitude, longitude: p.longitude}]);
});
return () => sub.remove();
}, []);

const last = path[path.length - 1];

return (
<MapView
style={{flex: 1}}
region={
last ? {...last, latitudeDelta: 0.01, longitudeDelta: 0.01} : undefined
}>
<Polyline coordinates={path} strokeWidth={4} />
</MapView>
);
}

Sync status badge​

SyncBadge.tsx
import React, {useEffect, useState} from 'react';
import {Text} from 'react-native';
import LocationTracker, {type SyncEvent} from 'react-native-background-location-pro-eval';

export function SyncBadge() {
const [e, setE] = useState<SyncEvent | null>(null);

useEffect(() => {
const sub = LocationTracker.onSyncStatus(setE);
return () => sub.remove();
}, []);

if (!e) return null;
if (!e.online) return <Text>Offline · {e.pending} saved on device</Text>;
if (e.retryAttempt > 0) {
const inSec = Math.max(0, Math.round((e.nextRetryAt - Date.now()) / 1000));
return <Text>Retrying in {inSec}s · {e.pending} pending</Text>;
}
return <Text>Synced {e.synced} · {e.pending} pending</Text>;
}

Send points to your backend​

The tracker uploads to your server natively, even while the app is killed, so you do not need to forward points from JS. Point it at your API and implement one endpoint:

LocationTracker.startTracking('https://api.example.com');
// -> POST https://api.example.com/api/location/track
server/index.js (Express)
const express = require('express');
const app = express();
app.use(express.json({limit: '1mb'}));

const seenBatches = new Set(); // use a database unique index in production

app.post('/api/location/track', (req, res) => {
const {batchId, deviceId, points} = req.body;

if (seenBatches.has(batchId)) {
return res.json({ok: true, duplicate: true});
}
seenBatches.add(batchId);

for (const p of points) {
console.log(deviceId, new Date(p.timestamp).toISOString(), p.lat, p.lng, `±${p.accuracy}m`);
}
res.json({ok: true, accepted: points.length});
});

app.listen(3000);

Respond 2xx to acknowledge, 5xx or 429 (optionally with Retry-After) to ask for a retry. The full contract is in Offline tracking and sync.

Already have an API with a different shape?

Put a small adapter endpoint in front of it, or use a serverless function that receives /api/location/track and forwards points to your existing service. In the licensed edition you can also change the request format directly in the native source.

Switch profiles by use case​

import LocationTracker, {type TrackingProfile} from 'react-native-background-location-pro-eval';

function profileFor(mode: 'workout' | 'delivery' | 'all-day'): TrackingProfile {
switch (mode) {
case 'workout':
return 'continuous_fitness';
case 'delivery':
return 'balanced';
case 'all-day':
return 'adaptive_low_power';
}
}

LocationTracker.setTrackingProfile(profileFor('delivery')); // applied live

Production-style sync policy​

import LocationTracker from 'react-native-background-location-pro-eval';

export function configureTracker() {
LocationTracker.setDemoMode(false); // 30 s sync tick
LocationTracker.setSyncPolicy(150, 600_000, 0); // 150 per batch, max 10 min backoff, never dead-letter
LocationTracker.setTrackingProfile('adaptive_low_power');
}

Settings are persisted, so calling this once at app start is enough. Calling it on every launch is harmless.

Restore UI state on launch​

Tracking may have continued, or been revived, while your app was closed. Read the native state instead of storing your own flag:

const {desired, running, pending} = LocationTracker.getStatus();
// desired: user wants tracking; running: collecting right now; pending: still on device

Health check screen​

TrackerHealth.tsx
import React from 'react';
import {Button, Text, View} from 'react-native';
import LocationTracker from 'react-native-background-location-pro-eval';

export function TrackerHealth() {
const s = LocationTracker.getStatus();

return (
<View style={{gap: 8, padding: 16}}>
<Text>Location permission: {s.authorization} {s.preciseLocation ? '(precise)' : '(approximate)'}</Text>
<Text>Background recovery available: {String(s.exactAlarmsAllowed)}</Text>
<Text>Battery unrestricted: {String(s.batteryUnrestricted)}</Text>
<Text>Motion: {s.motionState} (gate {s.motionGateActive ? 'parked' : 'active'})</Text>
<Text>Last sync error: {s.lastSyncError || 'none'}</Text>

{s.authorization !== 'always' && (
<Button title="Allow background location" onPress={() => LocationTracker.requestLocationPermission(true)} />
)}
{!s.exactAlarmsAllowed && (
<Button title="Fix background recovery" onPress={() => LocationTracker.openExactAlarmSettings()} />
)}
{!s.batteryUnrestricted && (
<Button title="Remove battery restrictions" onPress={() => LocationTracker.requestIgnoreBatteryOptimizations()} />
)}
</View>
);
}