iBeacon / AltBeacon library for React Native built on New Architecture (TurboModules + JSI). Real background scanning on Android via foreground service — what other libraries promised but never delivered.
Platform support: Android fully supported. iOS in development.
- New Architecture (TurboModules + JSI) — no legacy bridge
- Real background scanning on Android via foreground service
- iBeacon + AltBeacon support (~85% of the beacon market)
- Kalman filter for stable distance readings (optional)
- Configurable scan intervals
- Does not request permissions — respects your app's UX flow
npm install react-native-beacon-kitAll required permissions are automatically merged into your app's AndroidManifest.xml via autolinking — no manual changes needed for React Native CLI or Expo bare workflow.
Expo managed workflow — declare permissions explicitly in app.json:
{
"expo": {
"android": {
"permissions": [
"android.permission.ACCESS_FINE_LOCATION",
"android.permission.ACCESS_BACKGROUND_LOCATION",
"android.permission.BLUETOOTH_SCAN",
"android.permission.BLUETOOTH_CONNECT"
]
}
}
}Permissions are declared but not requested by the library — use react-native-permissions to request them at runtime before calling any scanning method.
import Beacon from 'react-native-beacon-kit';
// Configure once on app start
Beacon.configure({
betweenScanPeriod: 0,
foregroundService: true, // required for real background scanning
kalmanFilter: { enabled: true },
});
// Check permissions (does not request them)
const granted = await Beacon.checkPermissions();
// Start ranging
await Beacon.startRanging({
identifier: 'my-region',
uuid: 'FDA50693-A4E2-4FB1-AFCF-C6EB07647825',
});
// Listen for beacons
const sub = Beacon.onBeaconsRanged((event) => {
event.beacons.forEach((beacon) => {
console.log(beacon.uuid, beacon.major, beacon.minor);
console.log(beacon.distance); // meters
console.log(beacon.rssi); // dBm
/** @warning May be randomized on Android 10+ */
console.log(beacon.macAddress);
});
});
// Cleanup
sub.remove();
await Beacon.stopRanging({ identifier: 'my-region', uuid: '...' });Call once before starting any scan. All fields are optional.
Beacon.configure({
scanPeriod?: number, // how long the BLE radio actively scans, in ms (default: 5000)
betweenScanPeriod?: number, // how long the BLE radio rests between scans, in ms (default: 0)
foregroundService?: boolean, // enable real background scanning (default: false)
kalmanFilter?: {
enabled: boolean,
q?: number, // process noise — how much you trust movement (default: 0.008)
r?: number, // measurement noise — how much you trust RSSI (default: 0.1)
},
});scanPeriod vs betweenScanPeriod
scanPeriod is how long the BLE radio is on and detecting. betweenScanPeriod is how long it rests before the next scan. Beacons are reported once at the end of each active period.
|←── scanPeriod ──→|←── betweenScanPeriod ──→|←── scanPeriod ──→|
radio ON radio OFF radio ON
betweenScanPeriod: 0 means continuous scanning. Adding a rest period saves battery — scanPeriod: 5000, betweenScanPeriod: 3000 and scanPeriod: 8000, betweenScanPeriod: 0 both update every ~8s, but the first uses less power because the radio is off for 3s each cycle.
| Use case | scanPeriod | betweenScanPeriod |
|---|---|---|
| Real-time positioning | 1100 | 0 |
| Standard indoor navigation | 5000 | 0 |
| Background zone detection | 5000 | 10000 |
| Battery-sensitive background | 2000 | 30000 |
Returns true if all required permissions are granted. Does not request them.
Detects nearby beacons with RSSI and distance (~every 1s).
Detects region entry/exit. Battery efficient — use to wake up ranging when the user enters a zone.
const sub = Beacon.onBeaconsRanged((event) => {
// event.region — the active region
// event.beacons — array of detected beacons
});
sub.remove(); // unsubscribeconst sub = Beacon.onRegionStateChanged((event) => {
// event.region — the region
// event.state — 'inside' | 'outside'
});interface Beacon {
uuid: string;
major: number;
minor: number;
rssi: number; // signal strength in dBm
distance: number; // estimated distance in meters
txPower: number; // calibrated tx power of the beacon
/** @warning May be randomized on Android 10+ — use uuid + major + minor as unique identifier instead. */
macAddress: string;
timestamp: number;
}Background scanning on Android requires a foreground service — a persistent notification the user can see.
Enable it via configure({ foregroundService: true }).
Without this, Android will kill the scanning process when the app goes to background. This is what most other beacon libraries for React Native never implemented.
- Requires
ACCESS_BACKGROUND_LOCATIONfor background scanning (Android 10+) - Requires
BLUETOOTH_SCAN+BLUETOOTH_CONNECT(Android 12+) - Foreground service keeps scanning alive even when the app is killed
iOS support is in development. Android is fully supported.
When iOS is released, it will require:
NSLocationAlwaysAndWhenInUseUsageDescriptioninInfo.plistNSLocationWhenInUseUsageDescriptioninInfo.plist- "Location updates" background mode enabled in Xcode capabilities
Background ranging on iOS works differently from Android. iOS does not support
continuous background ranging — instead, use startMonitoring() to wake the app
when the user enters a region, then start ranging from that callback.
iOS gives ~10 seconds of execution time per region event.
MIT