Bluetooth Low Energy

How do you build a Bluetooth mesh chat app in React Native?

You build it in layers. Every phone needs native code that runs both Bluetooth LE roles, advertising and scanning, plus framing that splits messages to fit the link. Above that sits a relay layer with unique message IDs, a hop limit and duplicate suppression, then end-to-end encryption so relaying phones cannot read what they carry. React Native supplies the interface and app logic; the radio layers come from native modules you write or an SDK that ships them.

Learning objectives

After reading this article you will be able to:

  • List the layers a phone-to-phone mesh chat app needs, from link to app
  • Explain why every phone in a React Native mesh must run both BLE roles
  • Describe how message IDs, hop limits and duplicate checks keep relaying under control

The layers of a mesh chat app

A chat app between two phones in range is a direct link. A mesh chat app adds one thing: a message can pass through other phones running the same app to reach someone out of direct range. How does a message cross a mesh network? covers the general idea. In practice the app splits into layers:

  1. Link. Every phone advertises so others can find it, scans for others, and accepts and opens Bluetooth LE connections.
  2. Framing. Messages are split into pieces that fit the connection and put back together on the other side.
  3. Relay. Phones forward messages that are not for them, with limits so copies do not circulate forever.
  4. Store-and-forward. Messages for people not currently reachable wait on a device until a path appears.
  5. Security. Identity keys and end-to-end encryption, because strangers’ phones carry your messages.
  6. App. Contacts, conversations, storage, notifications and permission prompts.

React Native is a good fit for the last layer. Everything below it touches the radio or has to keep running when the screen is off, so it lives in native code.

The Bluetooth layer needs both roles

Bluetooth LE connections are asymmetric. A central scans and starts connections; a peripheral advertises and accepts them, usually acting as the GATT server that holds the data. Android’s BLE overview is direct about the consequence: two devices that only support the central role cannot talk to each other. In a mesh every phone has to be findable and able to find others, so each one runs both roles.

This is the first obstacle in React Native. BLE libraries written for a phone talking to a sensor only need the central role. The react-native-ble-plx README, for example, lists “communicating between phones using BLE (Peripheral support)” as unsupported. Can react-native-ble-plx connect two phones directly? explains the gap in detail.

The peripheral side comes from the platform APIs. On iOS, CBPeripheralManager publishes services into the local GATT database and advertises them. On Android, the BLE advertiser and BluetoothGattServer do the same jobs. React Native’s own guide to Turbo Native Modules shows how to expose native code like this to JavaScript, using a typed specification that Codegen turns into platform interfaces.

Framing belongs in the same native layer. Android describes GATT as a way to send and receive short pieces of data, so a chat message, and certainly a photo, has to be cut into chunks, sent in order and reassembled, with a way to notice when a piece goes missing.

Relaying across the mesh

Once phones can exchange frames, the relay layer decides what to do with each message. The usual pattern, described in multi-hop relay:

  • Give every message a unique ID when it is created.
  • Give it a hop limit, often called a time to live (TTL), and lower it by one at each hop.
  • When a phone receives a message, drop it if it has seen that ID before. If the message is addressed to this phone, deliver it. Otherwise, if hops remain, forward it to the phone’s other neighbours.

Without duplicate suppression, a message in a dense group would bounce between the same phones indefinitely. Without a hop limit it would spread further than it is useful. Messages for someone who is not reachable right now can wait in an outbox and be retried later, which is store-and-forward.

Bitchat is a useful open-source reference. Its README describes automatic peer discovery and multi-hop message relay over Bluetooth LE, a compact binary packet format designed for Bluetooth LE limits, and a Nostr fallback over the internet. How to build an app like Bitchat walks through its design.

Encryption and identity

In a mesh, your message passes through phones you do not control, so the relay layer should only ever see ciphertext. Each phone holds a long-term key pair, and a contact is identified by a public key rather than a username on a server. Users need a way to confirm that a key really belongs to the person they think it does, for example by comparing a code in person or accepting the first key seen and warning on change, called trust on first use.

One-to-one chats can use an authenticated key exchange between two keys. Group chats are harder, because members join, leave and lose devices. Messaging Layer Security, published as RFC 9420, is a standard for group key establishment with forward secrecy and post-compromise security, designed for exactly this case.

Building it or using an SDK

The work splits cleanly:

  • Write it yourself: native modules for both BLE roles on iOS and Android, framing, a relay protocol, an outbox, a key store and an encryption layer, plus testing on many devices. You control every detail and own every bug.
  • Use an SDK that ships the lower layers: your React Native code handles the app, and the SDK handles discovery, both radio roles, relaying and encryption.

Offline Protocol’s mesh SDK is one example of the second route. It runs inside the app on each phone, relays across multiple hops with a documented default initial TTL of 8 hops in version 0.27.0, and protects application messages with MLS. Whichever route you choose, test on real phones from both platforms, with the app in the foreground and in the background, before relying on it.

Frequently asked questions

Is this the same as Bluetooth Mesh?

No. Bluetooth Mesh is a separate specification from the Bluetooth SIG. A phone chat mesh like the one described here is built by the app itself, as its own protocol on top of ordinary Bluetooth LE connections.

Sources

Build it with Offline Protocol

The two-phone walkthrough builds a bare React Native app on the published mesh SDK package and exchanges messages between two phones over Bluetooth LE, with discovery and encryption handled by the SDK.

Read the two-phone app walkthrough