Firebase Cloud Messaging (FCM)

Integrate Firebase Cloud Messaging and display notifications with react-native-notify-kit.

Firebase Cloud Messaging (FCM) is a messaging solution for sending remote push notifications to Android and iOS devices. react-native-notify-kit ships a first-class integration called FCM Mode that handles the Android / iOS asymmetry for you and is the recommended path for new integrations.

FCM Mode is the recommended approach for FCM push with react-native-notify-kit. Use the legacy manual pattern at the bottom of this page only if you already have an existing integration and cannot migrate — new integrations should follow FCM Mode.

Why FCM Mode

FCM has two payload shapes and each has a problem:

  • notification payload: the FCM SDK auto-displays on Android. If you also call displayNotification from your background handler you get two tray entries, and custom data is routed only to the tap PendingIntent so the library's query APIs can't see it.
  • data-only payload: clean on Android, but iOS treats these as silent pushes and drops 30–60% on real devices.

FCM Mode picks the right payload per platform (data-only on Android, alert + mutable-content: 1 on iOS) but hides the asymmetry behind a single developer API. A Notification Service Extension on iOS reads the shared notifee_options blob and reshapes the notification so you keep full display control while getting APNs reliability.

See FCM Mode for the full architecture, payload schema, migration guide, and troubleshooting.

Prerequisites

See the React Native Firebase quick start and the messaging setup guide for the Firebase-side configuration.

Quick start (FCM Mode)

1. Server: build the payload

The server SDK ships with the main package under the react-native-notify-kit/server subpath export. It has zero runtime dependencies and is meant for Node.js / Firebase Cloud Functions.

// server/sendNotification.ts
import { buildNotifyKitPayload } from 'react-native-notify-kit/server';
import * as admin from 'firebase-admin';

admin.initializeApp();

export async function sendOrderUpdate(deviceToken: string, orderId: string) {
  const message = buildNotifyKitPayload({
    token: deviceToken,
    notification: {
      id: `order-${orderId}`,
      title: 'Your order is on the way',
      body: 'Tap to see live tracking.',
      data: { orderId },
      android: {
        channelId: 'orders',
        smallIcon: 'ic_notification',
        pressAction: { id: 'open-order', launchActivity: 'default' },
      },
      ios: {
        sound: 'default',
        interruptionLevel: 'timeSensitive',
      },
    },
    options: { ttl: 3600 },
  });

  await admin.messaging().send(message);
}

buildNotifyKitPayload emits one FCM HTTP v1 message that is data-only on Android and alert-style on iOS. Both platforms carry an identical notifee_options blob.

2. Android client: wire handleFcmMessage

handleFcmMessage is a one-liner that parses the server payload, reconstructs a Notification object, and displays it through the usual Notify Kit pipeline.

// index.js
import { AppRegistry } from 'react-native';
import messaging from '@react-native-firebase/messaging';
import notifee, { AndroidImportance } from 'react-native-notify-kit';
import App from './App';

notifee.setFcmConfig({
  defaultChannelId: 'default',
  defaultPressAction: { id: 'default', launchActivity: 'default' },
});

notifee.createChannel({ id: 'default', name: 'Default', importance: AndroidImportance.HIGH });
notifee.createChannel({ id: 'orders', name: 'Orders', importance: AndroidImportance.HIGH });

messaging().setBackgroundMessageHandler(async remoteMessage => {
  await notifee.handleFcmMessage(remoteMessage);
});

AppRegistry.registerComponent('MyApp', () => App);
// App.tsx — foreground
import { useEffect } from 'react';
import messaging from '@react-native-firebase/messaging';
import notifee from 'react-native-notify-kit';

export default function App() {
  useEffect(() => messaging().onMessage(m => notifee.handleFcmMessage(m)), []);
  // ...
}

3. iOS client: scaffold the Notification Service Extension

From your project root:

npx react-native-notify-kit init-nse

The CLI patches .pbxproj and Podfile automatically and generates a NotifyKitNSE target using NotifeeExtensionHelper. Supports --dry-run, --force, --target-name, --ios-path, and --bundle-suffix options. Rerun pod install and rebuild.

On iOS, handleFcmMessage is a no-op in background/killed (the NSE has already displayed the notification); in foreground it displays the banner as usual.

Next steps

For the full reference (server builders, client config, payload schema, migration from the manual pattern, troubleshooting, known limitations) see the FCM Mode guide.

Creating and storing device tokens

Independent of FCM Mode, you still need device tokens to target specific devices. React Native Firebase's messaging API handles this:

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

async function registerDeviceToken() {
  await messaging().registerDeviceForRemoteMessages();
  const token = await messaging().getToken();
  await postToApi('/users/me/tokens', { token });
}

Fetch and update the token on every app boot in case it has changed, and consider listening to onTokenRefresh while the app is open.

Legacy manual pattern (backward compatibility only)

This section is retained only for users with an existing integration built on the upstream Notifee manual pattern who cannot migrate right now. New integrations should use FCM Mode instead.

The legacy pattern bypasses FCM Mode's iOS NSE/alert handling, so iOS behavior differs from Android: silent pushes are throttled by APNs (30–60% drop rate is typical), there is no automatic attachment handling, and no foreground customization via NSE. These are inherent trade-offs of the approach and are the reason FCM Mode exists.

The pattern sends a data-only FCM payload with a serialized notification object and calls displayNotification on the client:

// Node.js
await admin.messaging().sendMulticast({
  tokens,
  data: {
    notifee: JSON.stringify({
      title: 'Your order has been shipped',
      body: 'This message was sent via FCM!',
      android: {
        channelId: 'default',
        pressAction: { id: 'default', launchActivity: 'default' },
      },
    }),
  },
});
// React Native
import notifee from 'react-native-notify-kit';
import messaging from '@react-native-firebase/messaging';

function onMessageReceived(message) {
  notifee.displayNotification(JSON.parse(message.data.notifee));
}

messaging().onMessage(onMessageReceived);
messaging().setBackgroundMessageHandler(onMessageReceived);

Every property in FCM data must be a string — that's why the notification object is JSON-stringified before sending.

If you're maintaining an existing integration like this and want to migrate, see the migration from the manual pattern section of the FCM Mode guide.