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

expo-ble-region

v0.3.0

Published

Native Expo module for iOS that enables BLE beacon region monitoring (iBeacon) with background enter/exit events.

Downloads

361

Readme

expo-ble-region

npm

Note: This module currently only works with Expo 54 (tested).

A native Expo module for iOS iBeacon region monitoring with foreground and background enter/exit events. Uses CoreLocation for beacon detection and integrates with Expo's TaskManager for headless background JavaScript execution — even when the app is terminated.

Features

  • Foreground monitoring — Real-time enter/exit/ranging events via JS listeners
  • Background monitoring — Headless JS task execution via expo-task-manager
  • State persistence — Survives app termination; iOS relaunches the app for region events
  • Smart event routing — Foreground events go to JS listeners, background events go to the task consumer (no duplicate notifications)
  • Debug eventsonDebug event stream for real-time observability of the native state machine
  • Ranging-based exit fallback — Detects beacon disappearance even when iOS's didExitRegion is delayed

Installation

npx expo install expo-ble-region
npx expo install expo-task-manager

Note: expo-notifications is not required by the library itself. The example app uses it to demonstrate background notifications, but you can handle enter/exit events however you like.

Add the required permissions to your app.json:

{
  "expo": {
    "ios": {
      "infoPlist": {
        "NSLocationAlwaysAndWhenInUseUsageDescription": "Allow app to monitor beacons in the background.",
        "NSLocationWhenInUseUsageDescription": "Allow app to monitor beacons while in use.",
        "NSBluetoothAlwaysUsageDescription": "Allow app to use Bluetooth to detect beacons.",
        "UIBackgroundModes": ["location", "bluetooth-central"]
      }
    }
  }
}

API

Permissions

import * as ExpoBleRegion from 'expo-ble-region';

// Get current authorization status
const status = await ExpoBleRegion.getAuthorizationStatus();
// Returns: 'notDetermined' | 'authorizedWhenInUse' | 'authorizedAlways' | 'denied' | 'restricted'

// Request permissions
await ExpoBleRegion.requestWhenInUseAuthorization();
await ExpoBleRegion.requestAlwaysAuthorization(); // Required for background monitoring

Foreground Monitoring

Listen for beacon events while the app is open:

import * as ExpoBleRegion from 'expo-ble-region';
import { useEffect } from 'react';

export default function App() {
  useEffect(() => {
    const enterSub = ExpoBleRegion.addListener('onEnterRegion', ({ region }) => {
      console.log('Entered region:', region);
    });

    const exitSub = ExpoBleRegion.addListener('onExitRegion', ({ region }) => {
      console.log('Exited region:', region);
    });

    const beaconSub = ExpoBleRegion.addListener('onBeaconsDetected', ({ beacons }) => {
      console.log('Beacons detected:', beacons);
    });

    const errorSub = ExpoBleRegion.addListener('onError', ({ error, region }) => {
      console.error('Error:', error, region);
    });

    return () => {
      enterSub.remove();
      exitSub.remove();
      beaconSub.remove();
      errorSub.remove();
    };
  }, []);

  const startScanning = () => {
    ExpoBleRegion.startScanning('YOUR-BEACON-UUID', {});
  };

  const stopScanning = () => {
    ExpoBleRegion.stopScanning();
  };

  // ...
}

Background Monitoring

Execute JavaScript when the app is backgrounded or terminated. Define the task outside your React component tree:

import * as TaskManager from 'expo-task-manager';
import * as Notifications from 'expo-notifications';
import * as ExpoBleRegion from 'expo-ble-region';

const BACKGROUND_TASK = 'BACKGROUND_BEACON_TASK';

// 1. Define the task at the top level
TaskManager.defineTask(BACKGROUND_TASK, async ({ data, error }) => {
  if (error) return;

  if (data) {
    const { eventType, region, beacons } = data as any;

    if (eventType === 'onEnterRegion') {
      await Notifications.scheduleNotificationAsync({
        content: { title: 'Entered Region', body: `Entered ${region}` },
        trigger: null,
      });
    }

    if (eventType === 'onExitRegion') {
      await Notifications.scheduleNotificationAsync({
        content: { title: 'Exited Region', body: `Exited ${region}` },
        trigger: null,
      });
    }
  }
});

// 2. Start scanning with the task
export default function App() {
  const start = async () => {
    await ExpoBleRegion.startScanningWithTask('YOUR-BEACON-UUID', BACKGROUND_TASK, {});
  };

  const stop = async () => {
    await ExpoBleRegion.stopScanningTask(BACKGROUND_TASK);
  };

  // ...
}

Bluetooth State

ExpoBleRegion.initializeBluetoothManager();

ExpoBleRegion.addListener('onBluetoothStateChanged', ({ state }) => {
  console.log('Bluetooth:', state); // 'poweredOn', 'poweredOff', etc.
});

Debug Events

Stream native state machine transitions to your JS code for debugging:

ExpoBleRegion.addListener('onDebug', ({ message }) => {
  console.log('[Debug]', message);
});

Events

| Event | Payload | Description | |-------|---------|-------------| | onEnterRegion | { region: string } | Device entered a beacon region | | onExitRegion | { region: string } | Device exited a beacon region | | onBeaconsDetected | { beacons: Beacon[] } | Ranging update (fires ~1/sec while in region) | | onBluetoothStateChanged | { state: string } | Bluetooth hardware state changed | | onError | { error: string, region?: string } | Monitoring or location error | | onDebug | { message: string } | Internal state machine transition |

Beacon Object

{
  uuid: string;
  major: number;
  minor: number;
  distance: number;  // Estimated distance in meters (-1 if unknown)
  rssi: number;      // Signal strength
}

iOS Behavior Notes

  • Exit delay: iOS intentionally delays didExitRegion by ~30 seconds to prevent false positives from signal bouncing. This module includes a ranging-based fallback that fires exit after 35 seconds of zero beacons.
  • Permission flow: iOS uses a two-step permission flow. Users first grant "When In Use", then iOS later prompts to upgrade to "Always". Background monitoring requires "Always" permission.
  • App termination: When the app is terminated, iOS continues monitoring. On a region event, iOS relaunches the app in the background and the module restores monitoring from persisted state.

Running the Example

  1. Clone the repo and install dependencies:

    npm install && npm run build
    cd example && npm install
  2. Replace XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX in example/App.tsx with your beacon's UUID.

  3. Build and run:

    Local (requires Mac):

    npx expo run:ios

    EAS Build (cloud / Windows):

    npx expo install eas-cli
    eas login
    eas build -p ios --profile development

Note: Beacon monitoring requires a physical iPhone — simulators do not support BLE.

Platform Support

This module is iOS only. Android is not supported.

Contributing

Contributions are welcome! Please ensure any added features remain un-opinionated and delegate complex logic to expo-task-manager.