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-nearby-connections

v1.1.0

Published

P2P connections library for Expo. Supports Android and iOS.

Readme

expo-nearby-connections

npm version License: MIT Platform: Android Platform: iOS

An Expo library for peer-to-peer connections between nearby devices. Uses Google Nearby Connections on Android and Apple Multipeer Connectivity on iOS.

Note: Cross-platform P2P between Android and iOS is not supported.

Compatible Versions

| expo-nearby-connections | expo | react-native | | :---------------------: | :---: | :----------: | | 1.1.0 | 55 | 0.83.x | | 1.0.0 | 51 | 0.73.x |

New Architecture required. expo-nearby-connections 1.1.0 uses Nitro Modules, which requires New Architecture (enabled by default since Expo SDK 52).

Installation

npx expo install expo-nearby-connections react-native-nitro-modules

Or with npm/yarn/pnpm:

npm install expo-nearby-connections react-native-nitro-modules
# or
yarn add expo-nearby-connections react-native-nitro-modules
# or
pnpm add expo-nearby-connections react-native-nitro-modules

Setup

1. Add the config plugin

In your app.json or app.config.ts:

{
  "plugins": [
    [
      "expo-nearby-connections",
      {
        "bonjourServicesName": "my-app",
        "localNetworkUsagePermissionText": "$(PRODUCT_NAME) needs local network access to discover nearby devices",
        "bluetoothUsagePermissionText": "$(PRODUCT_NAME) uses Bluetooth to discover and connect to nearby devices"
      }
    ]
  ]
}

All plugin options are optional. Without options, default permission strings are used.

| Option | Platform | Description | | ------ | -------- | ----------- | | bonjourServicesName | iOS | Bonjour service name (defaults to app name) | | localNetworkUsagePermissionText | iOS | NSLocalNetworkUsageDescription | | bluetoothUsagePermissionText | iOS | NSBluetoothAlwaysUsageDescription |

2. Prebuild

npx expo prebuild --clean

Run prebuild again whenever you change the plugin config.

Permissions

This library does not handle runtime permissions. Use react-native-permissions or similar.

iOS

import { PERMISSIONS, checkMultiple, requestMultiple } from "react-native-permissions";

const permissions = [
  PERMISSIONS.IOS.BLUETOOTH,
  PERMISSIONS.IOS.LOCAL_NETWORK,
];

Android

import { PERMISSIONS, checkMultiple, requestMultiple } from "react-native-permissions";

const permissions = [
  PERMISSIONS.ANDROID.ACCESS_COARSE_LOCATION,
  PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION,
  PERMISSIONS.ANDROID.BLUETOOTH_ADVERTISE,
  PERMISSIONS.ANDROID.BLUETOOTH_CONNECT,
  PERMISSIONS.ANDROID.BLUETOOTH_SCAN,
  PERMISSIONS.ANDROID.NEARBY_WIFI_DEVICES,
];

API

Types

enum Strategy {
  P2P_CLUSTER = 1,        // many-to-many mesh
  P2P_STAR = 2,           // one hub, multiple spokes (default)
  P2P_POINT_TO_POINT = 3, // 1-to-1
}

interface BasePeer {
  peerId: string;
  name: string;
}

Advertise

startAdvertise(name, strategy?)

Starts broadcasting so nearby discoverers can find this device.

import { startAdvertise, Strategy } from "expo-nearby-connections";

const peerId = await startAdvertise("My Device", Strategy.P2P_STAR);

stopAdvertise()

import { stopAdvertise } from "expo-nearby-connections";

await stopAdvertise();

Discover

startDiscovery(name, strategy?)

Starts scanning for nearby advertisers.

import { startDiscovery, Strategy } from "expo-nearby-connections";

const peerId = await startDiscovery("My Device", Strategy.P2P_STAR);

stopDiscovery()

import { stopDiscovery } from "expo-nearby-connections";

await stopDiscovery();

Connection

requestConnection(advertisePeerId)

Sends a connection request to an advertiser found via onPeerFound.

import { requestConnection } from "expo-nearby-connections";

await requestConnection(peerId);

acceptConnection(targetPeerId)

Accepts an incoming connection request (called on the advertiser side).

import { acceptConnection } from "expo-nearby-connections";

await acceptConnection(peerId);

rejectConnection(targetPeerId)

Rejects an incoming connection request.

import { rejectConnection } from "expo-nearby-connections";

await rejectConnection(peerId);

disconnect(targetPeerId?)

Disconnects from a connected peer.

  • Android: if targetPeerId is provided, disconnects only that endpoint; omitting it calls stopAllEndpoints().
  • iOS: targetPeerId is always ignored — MCSession.disconnect() terminates the entire session.
import { disconnect } from "expo-nearby-connections";

await disconnect(peerId); // Android: disconnect specific peer
await disconnect();       // Android: disconnect all / iOS: disconnect session

Messaging

sendText(targetPeerId, text)

Sends a UTF-8 text message to a connected peer.

import { sendText } from "expo-nearby-connections";

await sendText(peerId, "Hello!");

Events

All event listeners return an Unsubscribe function. Call it to remove the listener.

onPeerFound(callback)

Fires when a discoverer finds an advertiser.

import { onPeerFound } from "expo-nearby-connections";

const unsubscribe = onPeerFound(({ peerId, name }) => {
  console.log("Found:", name);
});

unsubscribe(); // cleanup

onPeerLost(callback)

Fires when a previously discovered advertiser goes out of range.

import { onPeerLost } from "expo-nearby-connections";

const unsubscribe = onPeerLost(({ peerId }) => {
  // remove from list
});

onInvitationReceived(callback)

Fires on the advertiser when a discoverer calls requestConnection.

import { onInvitationReceived } from "expo-nearby-connections";

const unsubscribe = onInvitationReceived(({ peerId, name }) => {
  // prompt user to accept/reject
});

onConnected(callback)

Fires on both sides when a connection is fully established.

import { onConnected } from "expo-nearby-connections";

const unsubscribe = onConnected(({ peerId, name }) => {
  // connection ready
});

onDisconnected(callback)

Fires when a peer disconnects.

import { onDisconnected } from "expo-nearby-connections";

const unsubscribe = onDisconnected(({ peerId }) => {
  // remove from connected list
});

onTextReceived(callback)

Fires when a text message arrives from a connected peer.

import { onTextReceived } from "expo-nearby-connections";

const unsubscribe = onTextReceived(({ peerId, text }) => {
  console.log("Message from", peerId, ":", text);
});

Usage example

Advertiser side

import { useEffect, useState } from "react";
import {
  startAdvertise,
  stopAdvertise,
  onInvitationReceived,
  onConnected,
  onDisconnected,
  acceptConnection,
} from "expo-nearby-connections";

function AdvertiserScreen() {
  const [myPeerId, setMyPeerId] = useState<string>();

  useEffect(() => {
    startAdvertise("My Device").then(setMyPeerId);
    return () => { stopAdvertise(); };
  }, []);

  useEffect(() => {
    const unsub = onInvitationReceived(({ peerId }) => {
      acceptConnection(peerId);
    });
    return unsub;
  }, []);
}

Discoverer side

import { useEffect, useState } from "react";
import {
  startDiscovery,
  stopDiscovery,
  onPeerFound,
  onPeerLost,
  requestConnection,
  type BasePeer,
} from "expo-nearby-connections";

function DiscovererScreen() {
  const [peers, setPeers] = useState<BasePeer[]>([]);

  useEffect(() => {
    startDiscovery("My Device");
    return () => { stopDiscovery(); };
  }, []);

  useEffect(() => {
    const unsubFound = onPeerFound((peer) => {
      setPeers((prev) => [...prev, peer]);
    });
    const unsubLost = onPeerLost(({ peerId }) => {
      setPeers((prev) => prev.filter((p) => p.peerId !== peerId));
    });
    return () => { unsubFound(); unsubLost(); };
  }, []);

  const connect = (peerId: string) => requestConnection(peerId);
}

Development

# from repo root
pnpm install
pnpm build

# run example app
cd example
pnpm install
pnpm prebuild --clean --no-install

# iOS
pnpm ios

# Android
pnpm android

Contributing

Contributions are welcome. Please open an issue first for major changes.

See CHANGELOG.md for release history.