Skip to main content
engage

App push in React Native

active
Audience: developerUpdated 2026-08-09

This how-to wires the @binoban/react-native bridge into your app's push setup, so Engage pushes are displayed and their delivery, taps, and dismissals reported. The bridge is a thin wrapper over the native SDKs, so the underlying behaviour matches Android and iOS.

Work through App push first — it covers the prerequisites, the shape of the integration, and the source marker the examples below branch on.

1. Configure the Android notification appearance

The bridge reads the notification icon and channel from AndroidManifest.xml meta-data. Add whichever you want to override inside <application>:

<meta-data
android:name="io.binoban.sdk.reactNative.default_notification_icon"
android:resource="@drawable/ic_notification" />
<meta-data
android:name="io.binoban.sdk.reactNative.push_channel_id"
android:value="binoban_engage" />
<meta-data
android:name="io.binoban.sdk.reactNative.push_channel_name"
android:value="Updates" />
<meta-data
android:name="io.binoban.sdk.reactNative.push_channel_description"
android:value="Order and account updates" />
Meta-data keyDefault if absent
…default_notification_iconYour app icon.
…push_channel_idDEFAULT_NOTIFICATION_CHANNEL_ID
…push_channel_nameGeneral
…push_channel_descriptionEmpty.

The same channel-importance rule applies as on native Android: the channel is created at IMPORTANCE_HIGH, and an existing channel with that id is reused as-is.

Declare POST_NOTIFICATIONS in the same manifest, and request it at runtime on Android 13+ — without it nothing is displayed:

import { PermissionsAndroid, Platform } from 'react-native';

if (Platform.OS === 'android' && PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS) {
await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
}

2. Register the token

Take the token from whichever push library you use and pass it straight through:

import { useBinoban } from '@binoban/react-native';

const { setDeviceToken } = useBinoban();

// wherever your push library hands you a new or refreshed token
setDeviceToken(token);

On iOS this must be the FCM registration token, not the APNs device token — Binoban delivers through Firebase.

Refresh callbacks only fire when the token is generated or rotated, so an app that already used push has one in hand that will never be re-announced. Fetch it once at startup so those installs register too:

import messaging from '@react-native-firebase/messaging';

messaging().getToken().then(setDeviceToken);
messaging().onTokenRefresh(setDeviceToken);

setDeviceToken is safe to call repeatedly — registering the same token twice is harmless.

3. Forward incoming messages

When your push library delivers a data message, hand the payload to the bridge. The SDK displays it and reports delivery:

import { useEffect } from 'react';
import messaging from '@react-native-firebase/messaging';
import { useBinoban } from '@binoban/react-native';

// Foreground: inside a component
const { notify } = useBinoban();

useEffect(() => {
const unsubscribe = messaging().onMessage(async (remoteMessage) => {
if (remoteMessage.data?.source === 'binoban') {
notify(remoteMessage.data);
} else {
// your own push handling
}
});
return unsubscribe;
}, [notify]);

onMessage fires only while the app is foregrounded. Binoban sends data-only messages, so a message that arrives while the app is backgrounded reaches your background handler instead — forward it there too, or those notifications never appear.

The background handler runs outside the component tree, so useBinoban() is not available there. Forward through the client createClient() returned instead, which means keeping that client in its own module rather than inside a component:

// src/binoban.ts — one client, importable from anywhere
import { createClient } from '@binoban/react-native';

export const binobanClient = createClient({ /* … */ });
// index.js — registered before the app renders
import { AppRegistry } from 'react-native';
import messaging from '@react-native-firebase/messaging';
import { binobanClient } from './src/binoban';
import App from './src/App';
import { name as appName } from './app.json';

messaging().setBackgroundMessageHandler(async (remoteMessage) => {
if (remoteMessage.data?.source === 'binoban') {
binobanClient.notify(remoteMessage.data);
} else {
// your own push handling
}
});

AppRegistry.registerComponent(appName, () => App);
Register it at module scope

Call setBackgroundMessageHandler outside your components and before AppRegistry.registerComponent. A push delivered to a backgrounded or terminated app has no component tree to run in, so registering it inside a useEffect silently loses every backgrounded notification.

As on the other platforms, the source check is for your branch, not the SDK's safety — notify ignores non-Binoban payloads by itself. Drop the check if this app receives no pushes other than Binoban's.

4. Handle taps

Subscribe for live interactions, and check once at startup for the tap that cold-started the app:

import { useEffect } from 'react';
import { useBinoban } from '@binoban/react-native';

const { onNotificationInteraction, getInitialNotificationInteraction } = useBinoban();

useEffect(() => {
// A tap that launched the app before JS was listening. Call once.
getInitialNotificationInteraction().then((interaction) => {
if (interaction?.uri) navigateTo(interaction.uri);
});

const sub = onNotificationInteraction((interaction) => {
if (interaction.type === 'CLICKED' && interaction.uri) {
navigateTo(interaction.uri);
}
// interaction.customData is the string map sent with the push
});

return () => sub.remove();
}, []);

Each interaction carries { notificationUuid, type, actionId, uri, customData, reason }, where type is DELIVERED | CLICKED | CLOSED | FAILED | OPENED. The SDK's own tracking is preserved automatically.

5. iOS host forwarding

On Android nothing further is needed — interactions reach JS on their own.

On iOS the SDK never registers a UNUserNotificationCenterDelegate. Something in your app must own that delegate and forward to the bridge — but if you use @react-native-firebase/messaging, it already owns it through Firebase's method swizzling, and you can skip this step. Handle it only when you took the delegate yourself (swizzling disabled, or another library claimed it).

Forward from JS by passing each callback's payload through:

const {
didReceiveNotificationResponse,
willPresentNotification,
didReceiveRemoteNotification,
} = useBinoban();

willPresentNotification(userInfo); // reports `delivered`
didReceiveNotificationResponse(userInfo, actionId, dismissed); // `clicked` / `closed`
didReceiveRemoteNotification(userInfo); // displays a data push

userInfo is the notification's raw payload dictionary, actionId is the tapped button's id (null for a body tap), and dismissed is true only on a swipe-away.

…or forward straight from a native AppDelegate to the SDK's Swift entry points (onWillPresentForwarded, onDidReceiveForwarded, onApplicationDidReceiveRemoteNotification, onNewToken) — see App push on iOS. The bridge still emits the interaction to JS either way. The three JS methods are no-ops on Android.

As on native iOS, the SDK does not open URLs — read interaction.uri and route it.

Verify it worked

  1. Your next flushed batch carries a bb_notification_registered event with the token.
  2. Send a test campaign from the panel. The notification appears, and bb_notification_delivered follows; tapping it produces bb_notification_clicked. On iOS, keep the app in the foreground for this check — delivered is reported from the foreground-presentation callback.

See Push events for the full list.

Troubleshooting

SymptomLikely cause
Nothing is displayed on AndroidPOST_NOTIFICATIONS not granted, or a pre-existing channel with that id sits below IMPORTANCE_HIGH.
Nothing is displayed on iOSBackground Modes → Remote notifications is off, so iOS never wakes the app for a data-only message.
Only foreground pushes appearsetBackgroundMessageHandler is not forwarding to notify.
The token never registersYou passed the APNs device token instead of the FCM token on iOS.
Taps do nothing on iOSExpected — the SDK does not open URLs there. Route interaction.uri in your handler.
Nothing reaches JS on iOSThe host is not forwarding its UNUserNotificationCenterDelegate callbacks — see step 5.

Next steps