Push notifications

Deliver push tokens to the SDK, configure native iOS and Android push handling, track notification clicks, and handle deeplinks. Setup spans three layers: JavaScript for token forwarding and deeplink listening, native iOS for APNs, and native Android for Firebase.

Integrating push notifications involves forwarding device tokens to the SDK from JavaScript, setting up native push services, and handling notification clicks and deep links. This guide will walk you through the process.

On this page


How delivery and click tracking work

Push delivery and click tracking are handled by the underlying native SDKs. ZMP sends the notification through APNs (iOS) or FCM (Android); the native SDK reports an on-device delivery receipt back to ZMP and attributes clicks to the originating campaign. The React Native layer's job is narrow: forward the device token to the SDK, and surface click and deep-link events to your JavaScript so you can route the user.

flowchart TB

    subgraph JS["JavaScript Layer"]
        APP["Your React Native App"]
    end

    subgraph Bridge["React Native Bridge"]
        RN["zetakit_reactnative"]
    end

    subgraph Native["Native SDK Layer"]
        direction LR
        IOS["Zeta iOS SDK"]
        ANDROID["Zeta Android SDK"]
    end

    subgraph Providers["Push Providers"]
        direction LR
        APNs
        FCM
    end

    ZMP["Zeta Marketing Platform"]

    %% Token registration (solid)
    APP -- "updateDeviceToken()" --> RN
    RN --> IOS & ANDROID
    IOS & ANDROID -- "register token" --> ZMP

    %% Notification delivery (dashed) — native-only, no bridge
    ZMP -. "send push" .-> APNs & FCM
    APNs -. "deliver" .-> IOS
    FCM -. "deliver" .-> ANDROID

    %% Click/deeplink events (thick) — after user tap
    IOS == "click / deeplink" ==> RN
    ANDROID == "click / deeplink" ==> RN
    RN == "event" ==> APP

Solid lines (→): token registration through the bridge. Dashed lines (⇢): notification delivery at the native layer — the bridge is not involved. Thick lines (⇒): click and deeplink events routed to JavaScript after a user tap.

Note: Notification delivery and display are handled entirely at the native layer by ZTPushService (Android) and the Notification Service Extension (iOS). The React Native bridge is only involved for token forwarding and for routing click/deeplink events to your JavaScript code after a user taps a notification.

For the full delivery lifecycle, the event matrix, and native diagnostics, see the platform push guides and their FAQs:

Tip: After an app update, re-forward the device token on the next launch (see Forward the device token) so ZMP continues to target the device.


Forward the device token in JavaScript

To target your device with push notifications from ZMP, you must forward the native push token to the SDK from your JavaScript code:

ZetaClient.user.updateDeviceToken('your-device-push-token-string');

Receive the token via the bridge (Android)

On Android, instead of reading the token from native code, you can let the SDK emit it to JavaScript. Call ZetaClient.push.fetchToken() and listen for ZetaCoreEvents.DeviceTokenDelegate, which delivers the current push token as { deviceToken }:

import { DeviceEventEmitter } from 'react-native';
import ZetaClient, { ZetaCoreEvents } from 'zetakit_reactnative';

const tokenListener = DeviceEventEmitter.addListener(
  ZetaCoreEvents.DeviceTokenDelegate,
  ({ deviceToken }) => {
    ZetaClient.user.updateDeviceToken(deviceToken);
  }
);

// Ask the SDK to emit the current token to the listener above.
ZetaClient.push.fetchToken();

iOS: fetchToken() is a no-op on iOS. Obtain the APNs token natively in your AppDelegate and forward it with ZetaClient.user.updateDeviceToken(). See Configure iOS native push setup.

Tip: On iOS, and on Android 13+, notifications require the user's explicit permission. Call ZetaClient.push.requestNotificationPermissions() to prompt the user before you rely on token delivery.


Configure iOS native push setup

The iOS side of your React Native application requires native configuration in the ios/ directory to handle APNs registration, token forwarding, and rich notification delivery tracking.

Add CocoaPods Dependencies

To support rich push notifications (images, videos, GIFs) and delivery tracking, add the ZetaNotificationService pod to your Notification Service Extension target in your ios/Podfile:

target 'NotificationServiceExtension' do
  pod 'ZetaNotificationService'
end

Run pod install in your ios directory after updating the Podfile.

Complete Native APNs Setup

All APNs registration, device token formatting, and Notification Service Extension code are implemented in native Swift.

For the complete, native setup guides, refer to:


Re-register the token after iOS app updates

During an app update, application(_:didRegisterForRemoteNotificationsWithDeviceToken:) is not guaranteed to fire automatically, so the SDK may not receive the current token after the update.

To ensure the SDK receives the latest token, explicitly re-register for remote notifications at launch in your native iOS code:

if UIApplication.shared.isRegisteredForRemoteNotifications {
    UIApplication.shared.registerForRemoteNotifications()
}

When the refreshed token arrives, forward it to the SDK from JavaScript with ZetaClient.user.updateDeviceToken() so ZMP continues to target the device.


Configure Android native push setup

The Android side of your React Native application requires native configuration in the android/ directory to handle Firebase Cloud Messaging (FCM) and customize notification appearance.

Add Firebase Dependencies

Add the Firebase Messaging dependency to your android/app/build.gradle file:

dependencies {
    implementation("com.google.firebase:firebase-messaging:24.1.0")
}

Complete Native FCM Setup

The SDK includes a built-in push service that automatically captures and registers device tokens. If you need to customize notification channels, colors, icons, or integrate a custom FirebaseMessagingService, you will configure these in your native Kotlin code.

For the complete, native setup guides, refer to:


Rich notification support

Rich notifications display images, GIFs, or videos in the notification banner.


Handle notification clicks in JavaScript (Android)

On Android, when a user taps a push notification, you can process the payload and track the click directly from your JavaScript code using the Promise-based handleNotificationsMessage API:

const handled = await ZetaClient.push.handleNotificationsMessage(response);

if (handled) {
  // The Zeta SDK successfully processed the notification click.
}

iOS: On iOS, track notification clicks natively via UNUserNotificationCenterDelegate. See Configure iOS native push setup and the iOS Push Notifications Guide.


Configure deep linking and navigation

ZMP campaigns can deliver deep links to navigate users to specific screens when they tap a notification.

Register the Deeplink Delegate

Set up the deep link delegate in your JavaScript code after the SDK initializes:

ZetaClient.initialize(config, () => {
  ZetaClient.push.setDeeplinkDelegate();
});

Platform Behavior: setDeeplinkDelegate() and notifyReactNativeNavigationReady() are iOS-only. On Android, handle deep links through the intent-based flow described in Handle Android deep links.

Signal Navigation Readiness

If you are using a navigation library like React Navigation, notify the SDK when your navigation container is fully loaded and ready to handle routing:

<NavigationContainer onReady={() => ZetaClient.push.notifyReactNativeNavigationReady()}>

Listen for Deeplink Events

Set up an event listener to receive deep link events and route the user to the target screen:

import { Platform, NativeEventEmitter, DeviceEventEmitter } from 'react-native';
import ZetaClient, { ZetaCoreEvents } from 'zetakit_reactnative';

const eventEmitter = Platform.select({
  ios: new NativeEventEmitter(ZetaClient.bridge),
  android: DeviceEventEmitter,
});

const deeplinkListener = eventEmitter?.addListener(
  ZetaCoreEvents.DeeplinkDelegate,
  (event) => {
    const { deeplink, extraInfo } = event;
    console.log('Navigate to:', deeplink);
    console.log('Additional campaign data:', extraInfo);
    
    // Perform your app's custom navigation routing here
  }
);

Handle Android deep links

On Android, deep links arrive as Android intents rather than through the DeeplinkDelegate flow. Read the intent that launched the app, and listen for intents that arrive while the app is already running.

When the app is launched from a notification (cold start), read the launching intent once with the Promise-based getInitialIntent():

const intent = await ZetaClient.push.getInitialIntent();

if (intent?.data) {
  // Route to the screen described by intent.data / intent.extras
}

When a notification is tapped while the app is already running, the SDK emits the intent as an event. Listen for both the initial and subsequent intents:

import { DeviceEventEmitter } from 'react-native';
import ZetaClient, { ZetaCoreEvents } from 'zetakit_reactnative';

const initialIntentListener = DeviceEventEmitter.addListener(
  ZetaCoreEvents.AndroidInitialIntent,
  (intent) => {
    // Same shape as getInitialIntent(): { action, type, data, scheme, extras }
  }
);

const newIntentListener = DeviceEventEmitter.addListener(
  ZetaCoreEvents.AndroidNewIntent,
  (intent) => {
    // Fired when a notification is tapped while the app is running
  }
);

Note: AndroidInitialIntent and AndroidNewIntent are Android-only. Use the DeeplinkDelegate flow above for iOS.


Next

Now that push notifications are configured, continue your journey:

See also