---
title: Cloud Messaging
description: >-
  Cloud Messaging: getMessaging/isSupported, Installation ID registration
  (register/unregister/onRegistered/onUnregistered), deprecated FCM tokens
  (getToken/deleteToken/onTokenRefresh), permissions, foreground/background
  handlers, topics, APNs helpers, auto-init, delivery-metrics export, Expo
  config plugin options, firebase.json keys, and RN payload types
  (RemoteMessage, Notification, MessagePriority).
next: /messaging/usage/ios-setup
previous: /functions/writing-deploying-functions
---
> ## Documentation Index
> Fetch the complete documentation index at: https://rnfirebase.io/llms.txt
> Use this file to discover all available pages before exploring further.

## Prerequisites

- `@react-native-firebase/app` installed and native config completed per [Getting Started](/) (Android `google-services.json`, iOS `GoogleService-Info.plist`, Expo config plugin, or React Native CLI setup). From v26, New Architecture is required for packages with a native bridge.
- Install Messaging: `npm install --save @react-native-firebase/messaging` or `yarn add @react-native-firebase/messaging`, then rebuild native apps (`cd ios && pod install` on iOS).
- **iOS:** complete [iOS Messaging Setup](/messaging/usage/ios-setup) (Push Notifications capability, Background Modes with Background fetch + Remote notifications, APNs key linked in Firebase Console) before expecting FCM delivery on a physical device.
- **Android API 33+:** request `POST_NOTIFICATIONS` (see [Android - Requesting permissions](/messaging/usage#android---requesting-permissions)) before expecting notification UI.
- Register `setBackgroundMessageHandler` in the app **entry file** (for example `index.js`), **before** `AppRegistry.registerComponent`, not inside a React component. See [Background & Quit state messages](/messaging/usage#background--quit-state-messages).

Example Expo Router screen: https://github.com/invertase/react-native-firebase/blob/main/test-expo/app/messaging.tsx

# Installation

```bash
# Install & setup the app module
yarn add @react-native-firebase/app

# Install the messaging module
yarn add @react-native-firebase/messaging

# If you're developing your app using iOS, run this command
cd ios/ && pod install
```

> iOS requires further configuration before you can start receiving and sending
> messages through Firebase. Read [setup iOS with Firebase Cloud Messaging](/messaging/usage/ios-setup).

> Use of the `sendMessage()` API and its associated listeners requires a custom `XMPP` server. Read [Messaging with XMPP](/messaging/usage/messaging-with-xmpp) for the device-to-device setup. `sendMessage()` is Android only.

# Expo

## Config plugin (Android notification icon and color)

Add `@react-native-firebase/messaging` to the `plugins` array of `app.json` or `app.config.js`, after `@react-native-firebase/app`, then run prebuild again:

```json
{
  "expo": {
    "plugins": [
      "@react-native-firebase/app",
      [
        "@react-native-firebase/messaging",
        {
          "android": {
            "notificationIcon": "./assets/notification-icon.png",
            "notificationColor": "#4CAF50"
          }
        }
      ]
    ]
  }
}
```

The plugin only edits `AndroidManifest.xml`. It does not enable the iOS capabilities or upload an APNs key (see [iOS Messaging Setup](/messaging/usage/ios-setup)).

- It adds `com.google.firebase.messaging.default_notification_icon` pointing at `@drawable/notification_icon`, and, when a color is found, `com.google.firebase.messaging.default_notification_color` pointing at `@color/notification_icon_color`. It does not create those resources, so they must exist in the Android project.
- It looks for the icon and color in this order: the `android.notificationIcon` and `android.notificationColor` plugin options, then the options of an `expo-notifications` plugin entry written in array form (`icon`, `color`), then the top-level `notification` field of the Expo config (`icon`, `color`).
- If no icon is found, prebuild logs a warning, because Android 8.0 and above need a notification icon to display notifications correctly.
- An existing manifest entry with the same name is left unchanged.

## iOS - Notifications entitlement

Since Expo SDK 51, Notifications entitlement is not always added to iOS projects during prebuild. If your project uses push notifications, you may need to add the aps-environment entitlement to your app config:

```json
{
  "expo": {
    "ios": {
      "entitlements": {
        "aps-environment": "production"
      }
    }
  }
}
```

## iOS - Remote notification

If you require `remote notification` on Expo, add this to your Expo `app.json` or `app.config.js`:

```json
{
  "expo": {
    "ios": {
      "infoPlist": {
        "UIBackgroundModes": ["remote-notification"]
      }
    }
  }
}
```

## Android - Google Play Notification Delegation

If you use the REST v1 APIs (used by the Firebase Admin SDKs) and your app is running on Android Q+ with Google Play services that support it, Google implements "Notification Delegation" for messages. Notification delegation is not compatible with react-native-firebase. Specifically, if your notifications are delegated via proxy to Play Services, then your messaging listeners will not be called.

To work around this incompatibility, react-native-firebase disables notification delegation by default, using the `AndroidManifest.xml` method listed as one of the options described here: https://firebase.google.com/docs/cloud-messaging/android/message-priority#proxy.

You may re-enable notification delegation if your use case requires it and you can accept the messaging listener methods not executing for delegated messages by setting `messaging_android_notification_delegation_enabled` to `true` in `firebase.json` (rebuild the Android app after changing it).

`isNotificationDelegationEnabled` and `setNotificationDelegationEnabled` read and update the delegation state at runtime. They are Android only: on iOS `isNotificationDelegationEnabled` reports `false` and `setNotificationDelegationEnabled` resolves without doing anything.

```js
import {
  getMessaging,
  isNotificationDelegationEnabled,
  setNotificationDelegationEnabled,
} from '@react-native-firebase/messaging';

const messaging = getMessaging();

console.log('Delegation enabled:', isNotificationDelegationEnabled(messaging));

await setNotificationDelegationEnabled(messaging, true);
```

## Installation ID messaging (optional)

Firebase Installation ID (FID) registration (`register` / `onRegistered`) is **off by default**. Opt in before calling it.
For the migration path (API mapping, opt-in, error codes, checklist), see
[Migrating to Installation ID](/messaging/migrating-to-installation-id).
Deep usage samples (native keys, Expo plugin, listeners): [Installation ID messaging](/messaging/server-integration#installation-id-messaging).

For bare React Native, opt in via `firebase.json`:

```json
// <projectRoot>/firebase.json
{
  "react-native": {
    "messaging_installation_id_enabled": true
  }
}
```

For Expo, use the plugin prop (or set the Android / iOS keys manually — see
[Installation ID messaging](/messaging/server-integration#installation-id-messaging)):

```json
{
  "expo": {
    "plugins": [
      [
        "@react-native-firebase/messaging",
        {
          "installationIdEnabled": true,
          "android": {
            "notificationIcon": "./assets/notification_icon.png",
            "notificationColor": "#ffffff"
          }
        }
      ]
    ]
  }
}
```

`messaging_installation_id_enabled` / `installationIdEnabled: true` map to Android meta-data
`firebase_messaging_installation_id_enabled` and iOS `FirebaseMessagingInstallationIdEnabled`.

# Platform support and New Architecture

{/* prettier-ignore */}
| | |
| --- | --- |
| **Platforms** | Android, iOS (native Firebase SDK) |
| **New Architecture** | **Required** from v26. See [Migrating to v26](/migrating-to-v26#new-architecture-requirement). |

All modular functions are exported on both platforms. Platform-specific ones behave as follows on the other platform:

{/* prettier-ignore */}
| API | Platform | On the other platform |
| --- | --- | --- |
| `getAPNSToken`, `setAPNSToken` | iOS | Android: `getAPNSToken` resolves `null`, `setAPNSToken` resolves without doing anything. |
| `registerDeviceForRemoteMessages`, `unregisterDeviceForRemoteMessages` | iOS | Android: resolve without doing anything. `isDeviceRegisteredForRemoteMessages` always returns `true` on Android. |
| `getDidOpenSettingsForNotification`, `setOpenSettingsForNotificationsHandler` | iOS | Android: `getDidOpenSettingsForNotification` resolves `false`, the handler is never called. |
| `getIsHeadless` | iOS | Android: resolves `false`. |
| `sendMessage`, `onMessageSent`, `onSendError` | Android | iOS: `sendMessage` throws an `Error` in JavaScript, the listeners never fire. |
| `onDeletedMessages` | Android | iOS: the listener never fires. |
| `isNotificationDelegationEnabled`, `setNotificationDelegationEnabled` | Android | iOS: `isNotificationDelegationEnabled` returns `false`, `setNotificationDelegationEnabled` resolves without doing anything. |

React Native Firebase also exports helpers with no firebase-js-sdk equivalent: `SDK_VERSION`, the token, registration and APNs helpers, topic subscribe APIs, background handlers, and the payload types (`RemoteMessage`, `Notification`, `MessagePriority`, and related). Each is explained in the section that uses it.

# What does it do

React Native Firebase provides native integration of Firebase Cloud Messaging (FCM) for both Android & iOS. FCM is a cost
free service, allowing for server-device and device-device communication. The React Native Firebase Messaging module provides
a simple JavaScript API to interact with FCM.

<YouTube id="sioEY4tWmLI" />

The module also provides basic support for displaying local notifications, to learn more view the [Notifications](/messaging/notifications) documentation.

# Usage

## Check support

`isSupported` reports whether Cloud Messaging can run on this device. On React Native it always resolves `true` on iOS. On Android it resolves `true` when Google Play services is available and `false` otherwise. The firebase-js-sdk `isSupported()` takes no messaging instance and checks browser Push API support instead; React Native Firebase takes the `Messaging` instance for API shape consistency with other modular calls.

```js
import { getMessaging, isSupported } from '@react-native-firebase/messaging';

const supported = await isSupported(getMessaging());
console.log('Messaging supported:', supported);
```

## Installation ID registration (latest)

This is the latest way to register a device with FCM. Use `register` to register this app instance with FCM, and `onRegistered` to receive its Firebase Installation ID (FID). Store the FID on your backend if you send to individual devices (see [Server Integration](/messaging/server-integration)).

Installation ID messaging is **off by default**. Opt in first (see [Installation ID messaging](#installation-id-messaging-optional)), otherwise `register` and `unregister` reject, and `onRegistered` and `onUnregistered` throw, with `messaging/installation-id-not-enabled`.

`register` does not register the device with APNs on iOS. Use auto-registration (the default) or [`registerDeviceForRemoteMessages`](#auto-registration-ios) so remote pushes can arrive.

`onRegistered` returns an unsubscribe function. A new subscriber receives the cached FID immediately while the app is still registered, and later registrations and FID rotations fire as usual.

```js
import { getMessaging, onRegistered, register } from '@react-native-firebase/messaging';

async function registerDevice() {
  const messaging = getMessaging();

  const unsubscribe = onRegistered(messaging, installationId => {
    console.log('Registered with FID:', installationId);
  });

  await register(messaging);

  // Call unsubscribe() when the listener is no longer needed, for example in an effect cleanup.
  return unsubscribe;
}
```

`unregister` removes this app instance's FCM registration so its current FID is no longer active. Use it when a user logs out (see [Signing out users](/messaging/server-integration#signing-out-users)). `onUnregistered` only fires for an `unregister` call that completes after you subscribe. Call `register` again to get a new registration.

```js
import {
  getMessaging,
  onUnregistered,
  register,
  unregister,
} from '@react-native-firebase/messaging';

async function resetRegistration() {
  const messaging = getMessaging();

  const unsubscribe = onUnregistered(messaging, installationId => {
    console.log('Unregistered FID:', installationId);
  });

  await unregister(messaging);
  await register(messaging);

  // Call unsubscribe() when the listener is no longer needed, for example in an effect cleanup.
  return unsubscribe;
}
```

## FCM registration tokens (deprecated)

The Messaging token APIs `getToken`, `deleteToken` and `onTokenRefresh` are deprecated. Firebase JS 12.14+, Android Messaging 25.1+ and Apple 12.18+ deprecated the FCM token APIs in favor of Installation ID registration (`register` / `onRegistered`, and `unregister` / `onUnregistered` in place of `deleteToken`). The token APIs still work while Installation ID messaging is off. Once you opt in, `getToken` and `deleteToken` reject and `onTokenRefresh` throws with `messaging/token-api-disabled`.

Use `getToken` to read the FCM registration token for this app instance, and `onTokenRefresh` when the token rotates. Store the token on your backend if you send to individual devices (see [Server Integration](/messaging/server-integration)).

`@react-native-firebase/messaging` still exports `GetTokenOptions`, but the web fields `vapidKey` and `serviceWorkerRegistration` are not meaningful on native. Use `NativeTokenOptions` (`appName` on Android, `senderId` on iOS) with `getToken` / `deleteToken`. Do not pass VAPID or service-worker options. When omitted, `appName` defaults to the app name and `senderId` defaults to the `messagingSenderId` of the app options. Both must be strings, otherwise the call throws.

On iOS, `getToken` rejects with `messaging/unregistered` when the app is not registered for remote notifications. Register first with auto-registration (the default) or with [`registerDeviceForRemoteMessages`](#auto-registration-ios).

`onTokenRefresh` returns an unsubscribe function. The listener is only called while the app is in the foreground.

```js
// DEPRECATED: prefer register() above. Still works while Installation ID messaging is off.
import { getMessaging, getToken, onTokenRefresh } from '@react-native-firebase/messaging';

const messaging = getMessaging();

const token = await getToken(messaging);
console.log('FCM token:', token);

const unsubscribe = onTokenRefresh(messaging, newToken => {
  console.log('FCM token refreshed:', newToken);
});

// Call the unsubscribe function when the listener is no longer needed.
unsubscribe();
```

`deleteToken` invalidates the current token immediately and permanently: messages sent to it fail. Use it when a user logs out (see [Signing out users](/messaging/server-integration#signing-out-users)). The next `getToken` call returns a new token.

```js
// DEPRECATED: prefer unregister() and register() above. Still works while Installation ID messaging is off.
import { getMessaging, deleteToken, getToken } from '@react-native-firebase/messaging';

const messaging = getMessaging();

await deleteToken(messaging);
const newToken = await getToken(messaging);
console.log('New FCM token:', newToken);
```

`register` has no equivalent of these per-platform options. For `getToken` and `deleteToken`, `NativeTokenOptions` overrides the defaults per platform:

```js
// DEPRECATED: register() takes no per-platform options. Still works while Installation ID messaging is off.
import { Platform } from 'react-native';
import { getMessaging, getToken } from '@react-native-firebase/messaging';

const messaging = getMessaging();

const token = await getToken(
  messaging,
  Platform.OS === 'ios' ? { senderId: 'YOUR_SENDER_ID' } : { appName: '[DEFAULT]' },
);
console.log('FCM token:', token);
```

## iOS - Requesting permissions

iOS prevents messages containing notification (or 'alert') payloads from being displayed unless you have received explicit permission from the user. Request it with `requestNotifications` from [react-native-permissions](https://github.com/zoontek/react-native-permissions). If your app uses Expo, use [expo-notifications](https://docs.expo.dev/versions/latest/sdk/notifications/) to request notification permission instead.

> To learn more about local notifications, view the [Notifications](/messaging/notifications) documentation.

```js
// codeblock-ignore: imports react-native-permissions, a third-party package that is not a dependency of React Native Firebase
import { requestNotifications } from 'react-native-permissions';

async function requestUserPermission() {
  const { status } = await requestNotifications(['alert', 'badge', 'sound']);
  const enabled = status === 'granted';

  if (enabled) {
    console.log('Authorization status:', status);
  }
}

await requestUserPermission();
```

The options passed to `requestNotifications` give finer control over how notifications are handled within your
application. To learn more, view the advanced [iOS Permissions](/messaging/ios-permissions) documentation.

## Android - Requesting permissions

On Android API level 32 and below, you do not need to request user permission. For API level 33+ you must request the `POST_NOTIFICATIONS` permission. `requestNotifications` from `react-native-permissions` does that on Android, or you can use React Native `PermissionsAndroid`:

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

async function requestAndroidNotificationPermission() {
  if (Platform.OS === 'android') {
    const result = await PermissionsAndroid.request(
      PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
    );
    console.log('POST_NOTIFICATIONS permission:', result);
  }
}

await requestAndroidNotificationPermission();
```

## Receiving messages

FCM messages can be sent to _real_ Android/iOS devices and Android emulators (iOS simulators however do _not_ handle cloud messages) via a number of methods (see below).
A message is simply a payload of data which can be used however you see fit within your application.

Common use-cases for handling messages could be:

- Displaying a notification (see [Notifications](/messaging/notifications)).
- Syncing message data silently on the device (e.g. via `AsyncStorage`).
- Updating the application's UI.

> To learn about how to send messages to devices from your own server setup, view the
> [Server Integration](/messaging/server-integration) documentation.

Depending on the device state, incoming messages are handled differently by the device and module. To understand these
scenarios, it is first important to establish the various states a device can be in:

{/* prettier-ignore */}
| State | Description |
| --- | --- |
| **Foreground** | When the application is open and in view. |
| **Background** | When the application is open, however in the background (minimized). This typically occurs when the user has pressed the "home" button on the device or has switched to another app via the app switcher. |
| **Quit** | When the device is locked or application is not active or running. The user can quit an app by "swiping it away" via the app switcher UI on the device. |

The user must have opened the app before messages can be received. If the user force quits the app from the device settings, it must be re-opened again before receiving messages.

Depending on the contents of the message, it's important to understand both how the device will handle the message (e.g. display a notification, or even ignore it) and also how the library sends events to your own listeners.

### Message handlers

The device state and message contents determine which handler will be called:

{/* prettier-ignore */}
| | Foreground | Background | Quit |
| --- | --- | --- | --- |
| **Notification** | `onMessage` | `setBackgroundMessageHandler` | `setBackgroundMessageHandler` |
| **Notification + Data** | `onMessage` | `setBackgroundMessageHandler` | `setBackgroundMessageHandler` |
| **Data** | `onMessage` | `setBackgroundMessageHandler` (**_see below_**) | `setBackgroundMessageHandler` (**_see below_**) |

- In cases where the message is data-only and the device is in the background or quit, both Android & iOS treat the message
  as low priority and will ignore it (i.e. no event will be sent). You can however increase the priority by setting the `priority` to `high` (Android) and
  `content-available` to `true` (iOS) properties on the payload.

- On iOS in cases where the message is data-only and the device is in the background or quit, the message will be delayed
  until the background message handler is registered via `setBackgroundMessageHandler`, signaling the application's JavaScript
  is loaded and ready to run. If the handler is not registered within 25 seconds, the message is dropped.

- On iOS, `setBackgroundMessageHandler` is driven by the native `application:didReceiveRemoteNotification:fetchCompletionHandler:` callback. For a message that has a `notification` payload and arrives while the app is in the background or quit, iOS shows the notification itself and only calls that callback when the message also sets `content-available` to `true`. Without it, your JavaScript does not run until the user taps the notification (then use `onNotificationOpenedApp` or `getInitialNotification`, see [Notifications](/messaging/notifications#handling-interaction)). The rows above for **Notification** and **Notification + Data** apply to Android, and to iOS only with `content-available`. Apple does not guarantee delivery of background notifications.

- When you send data messages to Apple devices, Firebase requires the APNs priority to be `5` (normal). A high priority data message is rejected by the FCM backend with `INVALID_ARGUMENT`. Keep the Android `priority` at `high` and the APNs `apns-priority` header at `5`.

- Reserved data keys: do not use `from`, `message_type`, or any key starting with `google.`, `gcm.` or `gcm.notification.` in the `data` payload. The maximum payload size is 4096 bytes.

To learn more about how to send these options in your message payload, view the Firebase documentation for your [FCM API implementation](https://firebase.google.com/docs/cloud-messaging/customize-messages/setting-message-priority).

### Notifications

The device state and message contents can also determine whether a [Notification](/messaging/notifications) will be displayed:

{/* prettier-ignore */}
| | Foreground | Background | Quit |
| --- | --- | --- | --- |
| **Notification** | Notification: &#10060; | Notification: &#9989; | Notification: &#9989; |
| **Notification + Data** | Notification: &#10060; | Notification: &#9989; | Notification: &#9989; |
| **Data** | Notification: &#10060; | Notification: &#10060; | Notification: &#10060; |

The foreground column describes the default. On iOS, a foreground notification message is presented when `messaging_ios_foreground_presentation_options` in `firebase.json` is set (see [Foreground Presentation Options (iOS)](#foreground-presentation-options-ios)). On Android, a foreground notification message is never displayed by FCM.

### Foreground state messages

To listen to messages in the foreground, call the `onMessage` method inside of your application code. Code
executed via this handler has access to React context and is able to interact with your application (e.g. updating the state or UI).

For example, the React Native [`Alert`](https://reactnative.dev/docs/alert) API could be used to display a new Alert
each time a message is delivered:

```jsx
import React, { useEffect } from 'react';
import { Alert, View, Text } from 'react-native';
import { getMessaging, onMessage } from '@react-native-firebase/messaging';

function App() {
  useEffect(() => {
    const messaging = getMessaging();
    const unsubscribe = onMessage(messaging, async remoteMessage => {
      Alert.alert('A new FCM message arrived!', JSON.stringify(remoteMessage));
    });

    return unsubscribe;
  }, []);

  return (
    <View>
      <Text>Cloud Messaging foreground listener registered</Text>
    </View>
  );
}

export default App;
```

The `remoteMessage` property contains all of the information about the message sent to the device from FCM, including
any custom data (via the `data` property) and notification data. To learn more, view the [`RemoteMessage`](https://reference.rnfirebase.io/_react-native-firebase/messaging/types/messaging/RemoteMessage.html)
API reference.

If the `RemoteMessage` payload contains a `notification` property when sent to the `onMessage` handler, Android does not show a notification to the user, and neither does iOS unless `messaging_ios_foreground_presentation_options` is configured (see [Foreground Presentation Options (iOS)](#foreground-presentation-options-ios)). Instead, you could trigger a local notification with a library of your choice
or update the in-app UI to signal a new notification.

### Deleted messages

`onDeletedMessages` is **Android only**: it never fires on iOS. It fires when the FCM server deletes pending messages for this app instance, which Firebase documents as happening when more than 100 messages are pending for the app on a device when it connects, or when the device has not connected to FCM for more than one month. When it fires, perform a full sync with your app server. There is no firebase-js-sdk modular equivalent.

```js
import { getMessaging, onDeletedMessages } from '@react-native-firebase/messaging';

const unsubscribe = onDeletedMessages(getMessaging(), () => {
  console.log('FCM deleted pending messages for this app instance');
});

unsubscribe();
```

### Background & Quit state messages

When the application is in a background or quit state, the `onMessage` handler will not be called when receiving messages.
Instead, you need to setup a background callback handler via the `setBackgroundMessageHandler` method.

**Agent failure mode (must get this right):** `setBackgroundMessageHandler` must be registered in the app **entry file** (typically `index.js`), **outside** any React component, and **before** `AppRegistry.registerComponent` runs. Registering it inside a component `useEffect` (or after the root component mounts) means quit/background messages are missed.

Full entry-file sample:

```js
// index.js: register the background handler BEFORE AppRegistry.registerComponent
import { AppRegistry } from 'react-native';
import { getMessaging, setBackgroundMessageHandler } from '@react-native-firebase/messaging';

const messaging = getMessaging();

setBackgroundMessageHandler(messaging, async remoteMessage => {
  // Runs in background and quit (Headless JS on Android; silent background launch on iOS).
  // Do not update React UI/state here. Network requests and local storage are fine.
  console.log('Message handled in the background!', remoteMessage);
});

function App() {
  return null;
}

AppRegistry.registerComponent('app', () => App);
```

The handler must return a promise once your logic has completed to free up device resources. It must not attempt to update
any UI (e.g. via state) - you can however perform network requests, update local storage etc.

On iOS the system is told the background work has finished when the returned promise settles, or after 25 seconds, whichever comes first. On Android the handler runs inside a Headless JS task that is stopped after a timeout (see [Background handler timeout (Android)](#background-handler-timeout-android)). If no handler has been registered when a background message arrives, a warning is logged and the message is not processed.

The `remoteMessage` property contains all of the information about the message sent to the device from FCM, including
any custom data via the `data` property. To learn more, view the [`RemoteMessage`](https://reference.rnfirebase.io/_react-native-firebase/messaging/types/messaging/RemoteMessage.html)
API reference.

If the `RemoteMessage` payload contains a `notification` property when sent to the `setBackgroundMessageHandler` handler, the device
will have displayed a [notification](/messaging/notifications) to the user.

#### Data-only messages

When an incoming message is "data-only" (contains no `notification` option), both Android & iOS regard it as low priority
and will prevent the application from waking (ignoring the message). To allow data-only messages to trigger the background
handler, you must set the "priority" to "high" on Android, and enable the `content-available` flag on iOS. For example,
if using the Node.js [`firebase-admin`](https://www.npmjs.com/package/firebase-admin) package to send a message:

```js
// codeblock-ignore: server-side sample that requires the firebase-admin package
const admin = require('firebase-admin');

admin.messaging().sendEachForMulticast({
  tokens: ['DEVICE_FCM_TOKEN'],
  data: {
    owner: JSON.stringify({ name: 'Owner' }),
    user: JSON.stringify({ name: 'User' }),
    picture: JSON.stringify({ name: 'Picture' }),
  },
  android: {
    // Required for background/quit data-only messages on Android
    priority: 'high',
  },
  apns: {
    payload: {
      aps: {
        // Required for background/quit data-only messages on iOS
        contentAvailable: true,
      },
    },
  },
});
```

For iOS-specific "data-only" messages, the message must include the appropriate APNs headers as well as the `content-available` flag in order to trigger the background handler. For example, if using the Node.js [`firebase-admin`](https://www.npmjs.com/package/firebase-admin) package to send a "data-only" message to an iOS device:

```js
// codeblock-ignore: server-side sample that requires the firebase-admin package
const admin = require('firebase-admin');

admin.messaging().send({
  data: {
    key: 'value',
  },
  apns: {
    payload: {
      aps: {
        contentAvailable: true,
      },
    },
    headers: {
      'apns-push-type': 'background',
      'apns-priority': '5',
      'apns-topic': 'com.example.app', // your app bundle identifier
    },
  },
  token: 'DEVICE_FCM_TOKEN',
});
```

View the [Sending Notification Requests to APNs](https://developer.apple.com/documentation/usernotifications/sending-notification-requests-to-apns) documentation to learn more about APNs headers.

These options can be applied to all FCM messages. View the [Server Integration](/messaging/server-integration) documentation
to learn more about other available SDKs.

#### Background Application State

Although the library supports handling messages in background/quit states, the underlying implementation on how this works is different on Android & iOS.

On Android, a [Headless JS](https://reactnative.dev/docs/headless-js-android) task (an Android only feature) is created that runs separately to your main React component; allowing your background handler code to run without mounting your root component.

On iOS however, when a message is received the device silently starts your application in a background state. At this point, your background handler (via `setBackgroundMessageHandler`) is triggered, but your root React component also gets mounted. This can be problematic for some users since any side-effects will be called inside of your app (e.g. `useEffect`s, analytics events/triggers etc). To get around this problem,
you can configure your `AppDelegate` to inject an `isHeadless` prop into your root component. Use this property to conditionally render `null` ("nothing") if your app is launched in the background:

```jsx
// index.js: background handler first, then HeadlessCheck wrapper for iOS silent launches
import React from 'react';
import { AppRegistry, View, Text } from 'react-native';
import { getMessaging, setBackgroundMessageHandler } from '@react-native-firebase/messaging';

const messaging = getMessaging();

setBackgroundMessageHandler(messaging, async remoteMessage => {
  console.log('Message handled in the background!', remoteMessage);
});

function App() {
  return (
    <View>
      <Text>Main app UI</Text>
    </View>
  );
}

function HeadlessCheck({ isHeadless }) {
  if (isHeadless) {
    // App has been launched in the background by iOS, ignore
    return null;
  }

  return <App />;
}

AppRegistry.registerComponent('app', () => HeadlessCheck);
```

Wire the native `isHeadless` prop in `AppDelegate` as described in [iOS Messaging Setup, Headless prop](/messaging/usage/ios-setup#headless-prop-appdelegate).

- For projects that use react-native-navigation (or if you prefer not to change launch props), call `getIsHeadless` from messaging. It is exported on both platforms: meaningful on iOS; on Android it always resolves `false`:

```js
import { getMessaging, getIsHeadless } from '@react-native-firebase/messaging';

const messaging = getMessaging();

getIsHeadless(messaging).then(isHeadless => {
  console.log('isHeadless:', isHeadless);
});
```

On Android, the `isHeadless` prop will not exist. Do not assume iOS background delivery matches Android Headless JS behavior.

#### iOS Background Limitation

On iOS devices, the user is able to toggle Background App Refresh in the device's Settings. Furthermore, the Background App Refresh setting will automatically be off if the device is in low power mode.

If the iOS Background App Refresh mode is off, your handler configured in `setBackgroundMessageHandler` will not be triggered.

### Topics

Topics are a mechanism which allow a device to subscribe and unsubscribe from named PubSub channels, all managed via FCM.
Rather than sending a message to a specific device by FCM token, you can instead send a message to a topic and any
devices subscribed to that topic will receive the message.

Topics allow you to simplify FCM [server integration](/messaging/server-integration) as you do not need to keep a store of
device tokens. There are however some things to keep in mind about topics:

- Messages sent to topics should not contain sensitive or private information. Do not create a topic for a specific user
  to subscribe to.
- Topic messaging supports unlimited subscriptions for each topic.
- One app instance can be subscribed to no more than 2000 topics.
- The frequency of new subscriptions is rate-limited per project. If you send too many subscription requests in a short
  period of time, FCM servers will respond with a 429 RESOURCE_EXHAUSTED ("quota exceeded") response. Retry with exponential backoff.
- A server integration can send a single message to multiple topics at once. This however is limited to 5 topics.

To learn more about how to send messages to devices subscribed to topics, view the [Send messages to topics](/messaging/server-integration#send-messages-to-topics)
documentation.

#### Subscribing to topics

To subscribe a device, call the `subscribeToTopic` method with the topic name. The name must be a string and must not include "/", otherwise the call throws an `Error` synchronously (before any native call). The returned promise rejects when the native subscription fails:

```js
import { getMessaging, subscribeToTopic } from '@react-native-firebase/messaging';

const messaging = getMessaging();

await subscribeToTopic(messaging, 'weather');
console.log('Subscribed to topic!');
```

#### Unsubscribing to topics

To unsubscribe from a topic, call the `unsubscribeFromTopic` method with the topic name. It applies the same name validation as `subscribeToTopic`:

```js
import { getMessaging, unsubscribeFromTopic } from '@react-native-firebase/messaging';

const messaging = getMessaging();

await unsubscribeFromTopic(messaging, 'weather');
console.log('Unsubscribed from the topic!');
```

## Message and notification payload shapes

Incoming handlers receive a [`RemoteMessage`](https://reference.rnfirebase.io/_react-native-firebase/messaging/types/messaging/RemoteMessage.html). That type is React Native Firebase-specific (firebase-js-sdk uses `MessagePayload` / `NotificationPayload` instead).

- `Notification` describes the optional `remoteMessage.notification` object (`title`, `body`, localization keys, plus `android` / `ios` platform fields). The image URL on Android is `notification.android.imageUrl`; the top-level `icon` and `image` fields are documented as web only.
- `MessagePriority` is the Android-only enum type on `remoteMessage.priority` / `originalPriority` (`PRIORITY_UNKNOWN` = 0, `PRIORITY_HIGH` = 1, `PRIORITY_NORMAL` = 2).
- `NotificationAndroidPriority` and `NotificationAndroidVisibility` are re-exported constant objects for Android notification `priority` / `visibility` fields on `Notification.android` (and for comparing values you read back).
- `NotificationIOSCriticalSound` is the object shape used when `notification.ios.sound` is a critical-alert sound (`name`, optional `critical`, optional `volume`) rather than a plain string.

```js
import {
  NotificationAndroidPriority,
  NotificationAndroidVisibility,
} from '@react-native-firebase/messaging';

console.log('Android notification priority max:', NotificationAndroidPriority.PRIORITY_MAX);
console.log('Android visibility public:', NotificationAndroidVisibility.VISIBILITY_PUBLIC);

/** @type {import('@react-native-firebase/messaging').NotificationIOSCriticalSound} */
const criticalSound = {
  name: 'default',
  critical: true,
  volume: 1.0,
};

/** @type {import('@react-native-firebase/messaging').Notification} */
const preview = {
  title: 'Hello',
  body: 'World',
  android: {
    priority: NotificationAndroidPriority.PRIORITY_HIGH,
    visibility: NotificationAndroidVisibility.VISIBILITY_PRIVATE,
  },
  ios: {
    sound: criticalSound,
  },
};

/** @type {import('@react-native-firebase/messaging').MessagePriority} */
const highPriority = 1; // MessagePriority.PRIORITY_HIGH

console.log('Notification preview title:', preview.title, 'priority sample:', highPriority);
```

# firebase.json

Messaging can be further configured to provide more control over how FCM is handled internally within your application. Keys go under the `react-native` object of the `firebase.json` file at the project root, and the native app must be rebuilt after changing them. The keys `messaging_android_notification_delivery_metrics_export_enabled` and `messaging_max_stored_notifications` (both Android only) are described in [firebase.json configuration](/app/json-config).

## Auto Registration (iOS)

React Native Firebase Messaging automatically registers the device with APNs to receive remote messages. If you need
to manually control registration you can disable this via the `firebase.json` file:

```json
{
  "react-native": {
    "messaging_ios_auto_register_for_remote_messages": false
  }
}
```

Once auto-registration is disabled you must manually call `registerDeviceForRemoteMessages` in your JavaScript code as
early as possible in your application startup:

```js
import {
  getMessaging,
  registerDeviceForRemoteMessages,
  isDeviceRegisteredForRemoteMessages,
} from '@react-native-firebase/messaging';

async function registerAppWithFCM() {
  const messaging = getMessaging();
  await registerDeviceForRemoteMessages(messaging);
  console.log('Registered:', isDeviceRegisteredForRemoteMessages(messaging));
}

await registerAppWithFCM();
```

When auto-registration is on (the default), calling `registerDeviceForRemoteMessages` logs a console warning that the call is not required.

`isDeviceRegisteredForRemoteMessages` is a synchronous read of a cached value. On iOS the value is read from the system when the Messaging module is created (it is always `false` on the iOS Simulator at that point) and is then updated by `registerDeviceForRemoteMessages` and `unregisterDeviceForRemoteMessages`. On Android it always returns `true`. `unregisterDeviceForRemoteMessages` removes the app's registration for remote notifications on iOS and does nothing on Android:

```js
import {
  getMessaging,
  unregisterDeviceForRemoteMessages,
  isDeviceRegisteredForRemoteMessages,
} from '@react-native-firebase/messaging';

const messaging = getMessaging();
await unregisterDeviceForRemoteMessages(messaging);
console.log('Still registered:', isDeviceRegisteredForRemoteMessages(messaging));
```

> **ARM64 iOS Simulator:** React Native Firebase intentionally does **not** call UIKit
> `registerForRemoteNotifications` on Apple Silicon Simulator builds (registration
> and auto-registration). Calling that API can block the
> main thread indefinitely. `registerDeviceForRemoteMessages` therefore stays pending until a
> ~10 second **global-queue** timer rejects with `messaging/registration-timeout`.
> You will not get a real APNs device token on that simulator, so use a physical iOS device
> when you need end-to-end push registration. If `registerDeviceForRemoteMessages` is called again
> before a prior attempt settles, the earlier promise rejects with
> `messaging/registration-superseded`. See
> [Migrating to v26, iOS APNs registration](/migrating-to-v26#ios-apns-registration-arm64-simulator--new-promise-rejections).

## APNs device tokens (iOS)

`getAPNSToken` and `setAPNSToken` are **iOS only**. Use `getAPNSToken` when another push provider (or your own server) needs the raw APNs device token rather than the Firebase Installation ID. It resolves the token as a hexadecimal string, or `null` when no token is available (and always `null` on Android). On iOS it rejects with `messaging/unregistered` when the app is not registered for remote notifications, and it resolves `null` on a simulator that has no APNs support.

Use `setAPNSToken` when your `AppDelegate` (or another library) receives the APNs token and you need to hand it to Firebase Messaging yourself. `token` must be a string and `type` is `'prod'`, `'sandbox'`, or `'unknown'` (the default). A non-string `token` or an invalid `type` throws an `Error` synchronously on both platforms. On iOS, `token` must also be a non-empty, even-length hexadecimal string, otherwise the call throws an `Error` before reaching the native module. On Android, `setAPNSToken` resolves without doing anything.

```js
import { Platform } from 'react-native';
import { getMessaging, getAPNSToken, setAPNSToken } from '@react-native-firebase/messaging';

async function syncApnsToken() {
  if (Platform.OS !== 'ios') {
    return;
  }

  const messaging = getMessaging();
  const token = await getAPNSToken(messaging);
  console.log('APNs token:', token);
}

async function handOverApnsToken(hexToken) {
  if (Platform.OS !== 'ios') {
    return;
  }

  await setAPNSToken(getMessaging(), hexToken, 'prod');
}

await syncApnsToken();
await handOverApnsToken('0123456789abcdef');
```

## iOS AppDelegate forwarding (proxy disabled)

If you set `FirebaseAppDelegateProxyEnabled` to `false` in your `Info.plist`, React Native Firebase no longer receives remote notification callbacks automatically. Forward these three methods from your `AppDelegate` to `RNFBMessagingAppDelegate.sharedInstance`. Forwarding `didRegisterForRemoteNotificationsWithDeviceToken` also passes the APNs token to Firebase Messaging.

With the proxy disabled, Firebase Messaging also needs to be told about each received message for its analytics, so call `appDidReceiveMessage` before forwarding `didReceiveRemoteNotification`, as shown below. To report foreground and tapped notifications too, set your own `UNUserNotificationCenter` delegate in `didFinishLaunchingWithOptions` and call `appDidReceiveMessage` in its `willPresent` and `didReceive` methods: React Native Firebase forwards those callbacks to a delegate that was set before it starts, but if your delegate implements `willPresent` it then controls foreground presentation instead of `messaging_ios_foreground_presentation_options`. See Firebase's [Receive messages in an Apple app](https://firebase.google.com/docs/cloud-messaging/ios/receive-messages) guide for the full list of steps when swizzling is disabled.

```objectivec
// AppDelegate.mm
#import <FirebaseMessaging/FirebaseMessaging.h>
#import <RNFBMessaging/RNFBMessaging+AppDelegate.h>

- (void)application:(UIApplication *)application
    didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
  [[RNFBMessagingAppDelegate sharedInstance] application:application
        didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}

- (void)application:(UIApplication *)application
    didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
  [[RNFBMessagingAppDelegate sharedInstance] application:application
        didFailToRegisterForRemoteNotificationsWithError:error];
}

- (void)application:(UIApplication *)application
    didReceiveRemoteNotification:(NSDictionary *)userInfo
          fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  [[FIRMessaging messaging] appDidReceiveMessage:userInfo];
  [[RNFBMessagingAppDelegate sharedInstance] application:application
                            didReceiveRemoteNotification:userInfo
                                  fetchCompletionHandler:completionHandler];
}
```

In Swift, add `#import <RNFBMessaging/RNFBMessaging+AppDelegate.h>` to your bridging header, then:

```swift
// AppDelegate.swift
import FirebaseMessaging

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
  RNFBMessagingAppDelegate.sharedInstance().application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
  RNFBMessagingAppDelegate.sharedInstance().application(application, didFailToRegisterForRemoteNotificationsWithError: error)
}

func application(
  _ application: UIApplication,
  didReceiveRemoteNotification userInfo: [AnyHashable: Any],
  fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
  Messaging.messaging().appDidReceiveMessage(userInfo)
  RNFBMessagingAppDelegate.sharedInstance().application(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler)
}
```

## Foreground Presentation Options (iOS)

React Native Firebase Messaging configures how to present a notification in a foreground app.
Refer to [UNNotificationPresentationOptions](https://developer.apple.com/documentation/usernotifications/unnotificationpresentationoptions) for the details.

The value is an array of any of `"badge"`, `"sound"`, `"alert"`, `"list"` and `"banner"`. The default is an empty array, so no notification is presented while the app is in the foreground. When `"list"` or `"banner"` is present, `"alert"` is ignored.

```json
{
  "react-native": {
    "messaging_ios_foreground_presentation_options": ["badge", "sound", "list", "banner"]
  }
}
```

These options only apply when no other `UNUserNotificationCenter` delegate (for example the one installed by `expo-notifications`) handles foreground presentation. When another delegate implements foreground presentation, it decides.

## Auto initialization

Firebase generates an Instance ID, which FCM uses to register this app instance via its FID when Installation ID messaging is
enabled (see [Migrating to Installation ID](/messaging/migrating-to-installation-id) and
[Installation ID messaging](/messaging/server-integration#installation-id-messaging)),
or to generate a registration token otherwise, and which Analytics uses for data collection.
When an Instance ID is generated, the library will upload the identifier and configuration data to Firebase. In most cases,
you do not need to change this behavior.

If you prefer to prevent Instance ID auto-generation, disable auto initialization for FCM and Analytics. `messaging_auto_init_enabled` applies to both Android and iOS:

```json
{
  "react-native": {
    "analytics_auto_collection_enabled": false,
    "messaging_auto_init_enabled": false
  }
}
```

To re-enable initialization (for example once permission is granted) call `setAutoInitEnabled(messaging, true)`. The value persists across app restarts once set, per the Firebase [iOS](https://firebase.google.com/docs/cloud-messaging/ios/client#prevent_auto_initialization) documentation. `setAutoInitEnabled` throws an `Error` if `enabled` is not a boolean. Read the current flag with `isAutoInitEnabled`:

```js
import {
  getMessaging,
  isAutoInitEnabled,
  setAutoInitEnabled,
} from '@react-native-firebase/messaging';

const messaging = getMessaging();
console.log('Auto-init enabled:', isAutoInitEnabled(messaging));
await setAutoInitEnabled(messaging, true);
```

## Delivery metrics export (BigQuery)

After you link FCM to BigQuery in the Firebase console ([Understand message delivery](https://firebase.google.com/docs/cloud-messaging/understand-delivery#bigquery-data-export)), you can enable per-app-instance delivery-metrics export from the client. `isDeliveryMetricsExportToBigQueryEnabled` reads the current flag. `experimentalSetDeliveryMetricsExportedToBigQueryEnabled` sets it (the name matches the firebase-js-sdk experimental helper). Both are exported on Android and iOS. On Android the setting is forwarded to the Firebase Messaging SDK. The Android default can also be set at build time with `messaging_android_notification_delivery_metrics_export_enabled` in `firebase.json`, see [firebase.json configuration](/app/json-config).

```js
import {
  getMessaging,
  isDeliveryMetricsExportToBigQueryEnabled,
  experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
} from '@react-native-firebase/messaging';

const messaging = getMessaging();
console.log('Export enabled:', isDeliveryMetricsExportToBigQueryEnabled(messaging));
await experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, true);
```

## Background handler timeout (Android)

On Android, a background event sent to `setBackgroundMessageHandler` has 60 seconds to resolve before it is automatically
canceled to free up device resources. If you wish to override this value, set the number of milliseconds in your config:

```json
{
  "react-native": {
    "messaging_android_headless_task_timeout": 30000
  }
}
```

## Notification Channel ID

On Android, any message which displays a [Notification](/messaging/notifications) uses a default Notification Channel
(created by FCM called "Miscellaneous"). This channel contains basic notification settings which may not be appropriate for
your application. You can change what Channel is used by updating the `messaging_android_notification_channel_id` property:

```json
{
  "react-native": {
    "messaging_android_notification_channel_id": "high-priority"
  }
}
```

Creating and managing Channels is outside of the scope of the React Native Firebase library.

## Notification Color

On Android, any messages which display a [Notification](/messaging/notifications) do not use a color to tint the content
(such as the small icon, title etc). To provide a custom tint color, update the `messaging_android_notification_color` property
with an Android color resource name.

The library provides a set of [predefined colors](https://github.com/invertase/react-native-firebase/blob/main/packages/messaging/android/src/main/res/values/colors.xml) corresponding to the [HTML colors](https://www.w3schools.com/colors/colors_names.asp) for convenience, for example:

```json
{
  "react-native": {
    "messaging_android_notification_color": "@color/hotpink"
  }
}
```

Note that only predefined colors can be used in `firebase.json`. If you want to use a custom color defined in your application resources, then you should set it in the `AndroidManifest.xml` instead (define the color in `android/app/src/main/res/values/colors.xml`, then reference it from `android/app/src/main/AndroidManifest.xml` with `com.google.firebase.messaging.default_notification_color` and `tools:replace="android:resource"`).

# Package version

`SDK_VERSION` is the React Native Firebase Messaging package version string exported from `@react-native-firebase/messaging`. The firebase-js-sdk does not export `SDK_VERSION` from `@firebase/messaging`.

```js
import { SDK_VERSION } from '@react-native-firebase/messaging';

console.log('Messaging package version:', SDK_VERSION);
```
