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

react-native-wifi-p2p-sync

v0.1.3

Published

Android WiFi Direct P2P discovery and data sync for React Native

Downloads

532

Readme

react-native-wifi-p2p-sync

Android WiFi Direct (P2P) discovery and data sync for React Native.

Devices running the same app automatically find each other over WiFi Direct, exchange data on connect, and relay live item updates via gossip. No internet or server required.

Android only. Requires a native build — run npx react-native run-android or expo run:android.


Installation

npm install react-native-wifi-p2p-sync

The library is fully self-contained with zero runtime dependencies. WiFi Direct management, DNS-SD discovery, TCP transport, and connection lifecycle are all handled by the library's own native module using standard Android and Java APIs.

Rebuild your Android app after installing:

npx react-native run-android
# or
expo run:android

Permissions

The library's AndroidManifest.xml declares the required permissions. Android's manifest merger will include them automatically, but you still need to request runtime permissions before calling start().

For reference, these are the permissions declared:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<!-- API 33+ -->
<uses-permission
    android:name="android.permission.NEARBY_WIFI_DEVICES"
    android:usesPermissionFlags="neverForLocation" />

Expo — if you need to add extra permissions to app.json:

{
  "expo": {
    "android": {
      "permissions": [
        "android.permission.ACCESS_FINE_LOCATION",
        "android.permission.NEARBY_WIFI_DEVICES",
        "android.permission.CHANGE_WIFI_STATE",
        "android.permission.ACCESS_WIFI_STATE",
        "android.permission.INTERNET"
      ]
    }
  }
}

Bare React Native — permissions are merged automatically. If you need to add them manually, add to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

Quick start

Wrap your app (or the relevant subtree) in <P2pSyncProvider>:

import { P2pSyncProvider } from 'react-native-wifi-p2p-sync';

export default function RootLayout() {
  return (
    <P2pSyncProvider
      serviceId="com.myapp.sync"          // unique per-app scope
      getData={() => db.getAllItems()}     // called when a peer connects
      onReceiveData={async (data) => {
        // JSON data from a peer on first connect
        await db.upsertMany(data as Item[]);
      }}
      onReceiveItem={(item) => {
        // single live update broadcast by a peer
        db.upsert(item);
      }}
    >
      <Stack />
    </P2pSyncProvider>
  );
}

API

<P2pSyncProvider>

Starts WiFi Direct discovery and manages the connection lifecycle.

| Prop | Type | Default | Description | |------|------|---------|-------------| | serviceId | string | — | DNS-SD service scope. Use a reverse-domain string unique to your app. | | getData | () => unknown \| Promise<unknown> | — | Returns local data (any JSON-serialisable value) to send when a peer connects. | | onReceiveData | (data: unknown) => void \| Promise<void> | — | Handles JSON data received from a peer. | | onReceiveItem? | (item: unknown) => void \| Promise<void> | — | Handles a single live item update. Falls back to onReceiveData(item) if omitted. | | onPeerConnected? | (peerId: string) => void | — | Called after handshake completes. | | onPeerDisconnected? | (peerId: string) => void | — | Called when a peer disconnects. | | gossipTtl? | number | 5 | Initial hop count for gossip broadcasts. | | seenSetExpiryMs? | number | 60000 | How long (ms) a seen-item entry is kept to suppress duplicate gossip. | | handshakeTimeoutMs? | number | 10000 | How long (ms) to wait for a peer handshake before disconnecting. |


Hooks

useP2pSync()

Primary hook — covers the common use case.

const { peers, discovered, status, isRunning, isSyncing, lastSyncAt, error, broadcast, restart } = useP2pSync();

useNearbyState()

Full internal state — useful for a debug or nearby-devices screen.

const {
  peers,            // Peer[]             — fully connected & handshaked
  discoveredDevices,// DiscoveredDevice[] — found but not yet connected
  incomingPeers,    // string[]           — connecting, awaiting handshake
  role,             // 'undecided' | 'hub' | 'spoke'
  status,           // SyncStatus
  error,            // string | null
  isSyncing,
  lastSyncAt,
  rawDeviceCount,
  myDeviceName,
  myDeviceAddress,
  restart,
} = useNearbyState();

Focused hooks

import {
  usePeers,              // → Peer[]
  useDiscoveredDevices,  // → DiscoveredDevice[]
  useSyncStatus,         // → SyncStatus
  useIsSyncing,          // → boolean
  useLastSyncAt,         // → number | null
  useSyncError,          // → string | null
} from 'react-native-wifi-p2p-sync';

broadcast(item)

Push a single item update to all connected peers. Relayed via gossip up to gossipTtl hops (default 5). Deduplication is handled automatically for items that have id and updatedAt fields.

const { broadcast } = useP2pSync();

// after saving locally:
broadcast({ id: 'abc', value: 42, updatedAt: Date.now() });

diffIncomingChanges(local, remote, options)

LWW (last-write-wins) merge helper. Returns only the remote items that are newer than or absent from local storage.

import { diffIncomingChanges } from 'react-native-wifi-p2p-sync';

onReceiveData: async (data) => {
  const local = await db.getAllItems();
  const toUpsert = diffIncomingChanges(local, data as Item[], {
    key: 'id',
    clock: 'updatedAt',
  });
  await db.upsertMany(toUpsert);
}

Types

type SyncStatus = 'idle' | 'starting' | 'active' | 'syncing' | 'error';
type NearbyRole  = 'undecided' | 'hub' | 'spoke';

interface Peer {
  peerId:  string;
  name:    string;
  status:  'found' | 'connecting' | 'connected' | 'disconnected';
}

interface DiscoveredDevice {
  address: string;
  name:    string;
  status:  'queued' | 'connecting';
}

How it works

  1. Discovery — Each device registers a DNS-SD service (_rnwifip2psync._tcp) embedding serviceId, deviceId, and deviceName in TXT records. Only peers with a matching serviceId are considered.
  2. Connection — The first device to discover a peer initiates a WiFi Direct connection. One device becomes the Group Owner (hub), the other a spoke. The library's native module registers a BroadcastReceiver to track group formation events. A TCP socket is opened over the group network.
  3. Handshake — Both sides exchange a HANDSHAKE message containing serviceId for app-level scoping. Mismatched apps are disconnected immediately.
  4. Sync — After handshake, both sides call getData() and send a SYNC_RESPONSE to the other. Each side calls onReceiveData with the peer's data and replies with ACK. When the ACK is received the session closes and both devices rejoin discovery.
  5. Live updatesbroadcast() sends an ITEM_UPDATE to all connected peers, which relay it with a decrementing TTL (gossip).

Compatibility

| React Native | Android API | Status | |---|---|---| | 0.73 – 0.82 | 24+ (Android 7+) | Supported | | New Architecture | 24+ | Supported |

The library uses its own self-contained native module and has no dependency on react-native-wifi-p2p or other third-party native wrappers.