Quick Start
Get background location tracking running in 5 minutes. This guide uses the hooks-first approach for the simplest integration.
Prerequisites
Before starting, make sure you have:
- Completed the Installation steps (package installed, Android permissions added)
- Completed the iOS Setup if targeting iOS
- A physical device for testing (recommended over emulators)
Minimal Example
The fastest way to get tracking working with three hooks:
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import {
useLocationPermissions,
useBackgroundLocation,
useLocationUpdates,
LocationAccuracy,
} from '@gabriel-sisjr/react-native-background-location';
function TrackingScreen() {
const { permissionStatus, requestPermissions, isRequesting } =
useLocationPermissions();
const { startTracking, stopTracking, isTracking, tripId } =
useBackgroundLocation();
const { locations, lastLocation } = useLocationUpdates({
onLocationUpdate: (location) => {
console.log('New location:', location.latitude, location.longitude);
},
});
// Step 1: Request permissions if not granted
if (!permissionStatus.location.hasPermission) {
return (
<View style={styles.container}>
<Text style={styles.title}>Location Permission Required</Text>
<Text style={styles.subtitle}>
Background location access is needed to track your trips.
</Text>
<Button
title={isRequesting ? 'Requesting...' : 'Grant Permissions'}
onPress={requestPermissions}
disabled={isRequesting}
/>
</View>
);
}
// Step 2: Show tracking controls
return (
<View style={styles.container}>
<Text style={styles.title}>
{isTracking ? 'Tracking Active' : 'Ready to Track'}
</Text>
{tripId && <Text style={styles.info}>Trip: {tripId}</Text>}
<Text style={styles.info}>Locations: {locations.length}</Text>
{lastLocation && (
<Text style={styles.info}>
Last: {lastLocation.latitude}, {lastLocation.longitude}
</Text>
)}
<Button
title={isTracking ? 'Stop Tracking' : 'Start Tracking'}
onPress={() =>
isTracking
? stopTracking()
: startTracking(undefined, {
accuracy: LocationAccuracy.HIGH_ACCURACY,
distanceFilter: 50,
})
}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', padding: 20 },
title: { fontSize: 22, fontWeight: 'bold', textAlign: 'center', marginBottom: 12 },
subtitle: { fontSize: 14, textAlign: 'center', marginBottom: 20, color: '#666' },
info: { fontSize: 14, textAlign: 'center', marginBottom: 8 },
});
export default TrackingScreen;
That is all you need. The hooks handle permissions, tracking lifecycle, session recovery, and real-time location streaming.
Step-by-Step Breakdown
1. Request Permissions
The useLocationPermissions hook manages the entire cross-platform permission flow:
import { useLocationPermissions } from '@gabriel-sisjr/react-native-background-location';
const { permissionStatus, requestPermissions, isRequesting } =
useLocationPermissions();
// Check if location permissions are granted
if (permissionStatus.location.hasPermission) {
// Ready to start tracking
}
// Request all permissions (location + notification)
const granted = await requestPermissions();
On Android, requestPermissions() handles the multi-step flow automatically:
- Requests foreground location (fine + coarse)
- Requests background location separately (Android 10+)
- Requests notification permission (Android 13+)
On iOS, it handles the WhenInUse-to-Always escalation and notification authorization.
See the Permissions guide for the full breakdown.
2. Start Tracking
The useBackgroundLocation hook provides tracking control with automatic session recovery:
import {
useBackgroundLocation,
LocationAccuracy,
} from '@gabriel-sisjr/react-native-background-location';
const { startTracking, stopTracking, isTracking, tripId, locations } =
useBackgroundLocation();
// Start with default options (auto-generated trip ID)
const id = await startTracking();
// Start with custom options
const id = await startTracking(undefined, {
accuracy: LocationAccuracy.HIGH_ACCURACY,
updateInterval: 5000,
distanceFilter: 50,
});
// Start with a custom trip ID
const id = await startTracking('my-trip-123', {
accuracy: LocationAccuracy.BALANCED_POWER_ACCURACY,
});
Note: The hook automatically checks for an active tracking session on mount. If your app was killed and restarted,
isTrackingandtripIdwill reflect the recovered session.
3. Listen for Real-Time Updates
The useLocationUpdates hook provides event-driven location streaming:
import { useLocationUpdates } from '@gabriel-sisjr/react-native-background-location';
const { locations, lastLocation, isTracking } = useLocationUpdates({
onLocationUpdate: (location) => {
console.log('Lat:', location.latitude, 'Lng:', location.longitude);
},
onLocationWarning: (warning) => {
console.warn('Warning:', warning.type, warning.message);
},
onNotificationAction: (event) => {
console.log('Notification action:', event.actionId);
},
});
4. Retrieve Stored Locations
Locations are persisted to the device database. You can retrieve them at any time:
import { getLocations } from '@gabriel-sisjr/react-native-background-location';
const locations = await getLocations('my-trip-123');
locations.forEach((loc) => {
console.log({
lat: parseFloat(loc.latitude),
lng: parseFloat(loc.longitude),
time: new Date(loc.timestamp),
accuracy: loc.accuracy,
});
});
Important:
latitudeandlongitudeare returned as strings to preserve decimal precision. Always useparseFloat()when passing coordinates to map libraries.
5. Stop Tracking
import { stopTracking } from '@gabriel-sisjr/react-native-background-location';
await stopTracking();
Or with the hook:
const { stopTracking } = useBackgroundLocation();
await stopTracking();
6. Clean Up Trip Data
After uploading locations to your server, clean up local storage:
import { clearTrip } from '@gabriel-sisjr/react-native-background-location';
await clearTrip('my-trip-123');
Complete App Example
A more complete example with error handling, notification customization, and server upload:
import React, { useEffect } from 'react';
import { View, Text, Button, Alert, StyleSheet } from 'react-native';
import {
useLocationPermissions,
useBackgroundLocation,
useLocationUpdates,
LocationAccuracy,
NotificationPriority,
type TrackingOptions,
} from '@gabriel-sisjr/react-native-background-location';
const TRACKING_OPTIONS: TrackingOptions = {
accuracy: LocationAccuracy.HIGH_ACCURACY,
updateInterval: 5000,
fastestInterval: 3000,
distanceFilter: 50,
notificationOptions: {
title: 'Trip Recording',
text: 'Recording your route in the background',
priority: NotificationPriority.LOW,
channelName: 'Trip Tracking',
},
};
export default function App() {
const { permissionStatus, requestPermissions, isRequesting } =
useLocationPermissions();
const {
startTracking,
stopTracking,
isTracking,
tripId,
locations,
error,
clearError,
refreshLocations,
clearCurrentTrip,
} = useBackgroundLocation({
onTrackingStart: (id) => console.log('Tracking started:', id),
onTrackingStop: () => console.log('Tracking stopped'),
onError: (err) => Alert.alert('Tracking Error', err.message),
});
const { lastLocation } = useLocationUpdates({
onLocationUpdate: (loc) => {
console.log(`[${new Date(loc.timestamp).toISOString()}]`,
loc.latitude, loc.longitude);
},
onLocationWarning: (warning) => {
console.warn(`[${warning.type}] ${warning.message}`);
},
});
// Show errors
useEffect(() => {
if (error) {
Alert.alert('Error', error.message, [
{ text: 'Dismiss', onPress: clearError },
]);
}
}, [error, clearError]);
const handleToggleTracking = async () => {
if (isTracking) {
await stopTracking();
} else {
await startTracking(undefined, TRACKING_OPTIONS);
}
};
const handleUploadAndClear = async () => {
if (!tripId || locations.length === 0) return;
try {
// Upload to your server
await fetch('https://your-api.com/trips', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
tripId,
locations: locations.map((loc) => ({
latitude: parseFloat(loc.latitude),
longitude: parseFloat(loc.longitude),
timestamp: loc.timestamp,
})),
}),
});
await clearCurrentTrip();
Alert.alert('Success', 'Trip data uploaded and cleared');
} catch (err) {
Alert.alert('Upload Failed', 'Please try again later');
}
};
// Permission screen
if (!permissionStatus.location.hasPermission) {
return (
<View style={styles.container}>
<Text style={styles.title}>Location Permission Required</Text>
<Button
title={isRequesting ? 'Requesting...' : 'Grant Permissions'}
onPress={requestPermissions}
disabled={isRequesting}
/>
</View>
);
}
return (
<View style={styles.container}>
<Text style={styles.title}>
{isTracking ? 'Tracking Active' : 'Ready'}
</Text>
{tripId && <Text style={styles.info}>Trip: {tripId}</Text>}
<Text style={styles.info}>Points: {locations.length}</Text>
{lastLocation && (
<Text style={styles.info}>
Last: {lastLocation.latitude}, {lastLocation.longitude}
</Text>
)}
<View style={styles.buttonGroup}>
<Button
title={isTracking ? 'Stop' : 'Start'}
onPress={handleToggleTracking}
/>
{isTracking && (
<Button title="Refresh" onPress={refreshLocations} />
)}
{!isTracking && locations.length > 0 && (
<Button title="Upload & Clear" onPress={handleUploadAndClear} />
)}
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', padding: 20 },
title: { fontSize: 22, fontWeight: 'bold', textAlign: 'center', marginBottom: 16 },
info: { fontSize: 14, textAlign: 'center', marginBottom: 8 },
buttonGroup: { marginTop: 20, gap: 12 },
});
Quick Tips
Distance Filter for Battery Efficiency
Set distanceFilter to reduce battery usage by only recording locations when the device has moved a minimum distance:
| Use Case | Recommended Distance Filter |
|---|---|
| Walking / running | 10 -- 25 meters |
| Driving | 50 -- 100 meters |
| Stationary with movement detection | 5 -- 10 meters |
Callback Throttling
Use onUpdateInterval in hooks to throttle expensive operations (like server sync) without affecting location collection frequency:
const { locations } = useLocationUpdates({
onUpdateInterval: 30000, // Callback fires every 30 seconds
onLocationUpdate: async (location) => {
// Expensive server sync -- only called every 30s
await syncToServer(location);
},
});
Locations are still collected at the native updateInterval rate and stored locally.
Trip ID Handling
startTracking returns the trip ID it used. Pass an explicit tripId to control the value, or omit it to let the library generate one:
import { startTracking } from '@gabriel-sisjr/react-native-background-location';
// Default: auto-generated trip ID
const autoTripId = await startTracking(options);
// Alternative: explicit trip ID
const explicitTripId = uuid.v4();
await startTracking(explicitTripId, options);
Foreground-Only Mode
If your app does not need background tracking, skip the background location permission entirely:
const id = await startTracking(undefined, {
foregroundOnly: true,
});
Coordinates are Strings
Coordinates are returned as strings to preserve full decimal precision across the TurboModule bridge. Always parse when using with map libraries:
// Correct
<MapView.Marker
coordinate={{
latitude: parseFloat(location.latitude),
longitude: parseFloat(location.longitude),
}}
/>
Testing
On Android Device
- Run on a real Android device (recommended)
- Grant all location permissions when prompted
- Start tracking and minimize the app
- Move around with your device
- Open the app and check collected locations
On iOS Device
- Run on a real iOS device (recommended) or use the Simulator with location simulation
- Grant "While Using" location permission, then grant "Always" when prompted
- Start tracking and minimize the app
- Move around with your device
- Open the app and check collected locations
On Emulator / Simulator
- Android Emulator: Use the extended controls (three dots) to set GPS coordinates or simulate routes
- iOS Simulator: Use Debug > Location > Custom Location or set a GPX file in your Xcode scheme
Background tracking behavior is difficult to test accurately on emulators. Always validate on a physical device before release.
Testing Notes
- Always test on a real device -- emulator/simulator GPS is unreliable
- Android: Background permission is critical for Android 10+
- Android: A foreground notification will appear when tracking is active
- iOS: A blue status bar indicator will appear when tracking in the background
- iOS: Use Xcode's location simulation (GPX files) for consistent test data
- Battery usage can be significant with high-frequency updates on both platforms
Common Issues
| Problem | Cause | Fix |
|---|---|---|
| Not tracking in background (Android) | Missing ACCESS_BACKGROUND_LOCATION | Grant "Allow all the time" in settings |
| Not tracking in background (iOS) | Only "While Using" permission | Request "Always" via requestPermissions() |
| No locations captured | GPS disabled or poor signal | Enable GPS, test outdoors, wait a few seconds after starting |
| Build error after install (Android) | Stale caches | See Android clean build steps below |
| Build error after install (iOS) | Stale caches or pods | See iOS clean build steps below |
Build Error Recovery
Android:
cd android && ./gradlew clean && cd ..
yarn start --reset-cache
yarn android
iOS:
cd ios && pod install && cd ..
yarn start --reset-cache
yarn example ios
Next Steps
- Permissions -- Deep dive into the cross-platform permission model
- iOS Setup -- iOS-specific configuration
- Hooks Guide -- Complete hooks documentation with all options
- Background Tracking Guide -- Production patterns and best practices
- Google Play Compliance -- Required steps before publishing to the Play Store