# How to Handle Firebase Cloud Messaging in React Native

- Tool: Firebase
- Difficulty: Intermediate
- Time required: 20-30 min
- Compatibility: React Native 0.72+, @react-native-firebase/messaging v20+, Firebase Blaze plan (recommended)
- Last updated: March 2026

## TL;DR

To handle Firebase Cloud Messaging in React Native, install @react-native-firebase/messaging, configure iOS APNs certificates and Android notification channels, request notification permissions, retrieve the FCM token with getToken(), and set up foreground and background message handlers. Use onMessage for foreground notifications and setBackgroundMessageHandler for background delivery. On iOS, upload your APNs key in the Firebase Console for push notifications to work.

## Implementing Firebase Cloud Messaging in React Native

Firebase Cloud Messaging (FCM) enables push notifications for React Native apps on both iOS and Android. This tutorial covers the full setup using the @react-native-firebase/messaging library, from installing native dependencies to handling messages in every app state: foreground, background, and quit. You will configure platform-specific requirements and build a complete notification handling flow.

## Before you start

- A React Native project (0.72 or later) with @react-native-firebase/app configured
- A Firebase project with Cloud Messaging enabled
- For iOS: an Apple Developer account with APNs key uploaded to Firebase Console
- For Android: google-services.json added to android/app directory

## Step-by-step guide

### 1. Install the messaging package

Add the @react-native-firebase/messaging package to your React Native project. This package provides the JavaScript API for FCM and the native modules for iOS and Android. After installing, run pod install for iOS to link the native dependencies. On Android, the native module is auto-linked.

```
npm install @react-native-firebase/messaging

# For iOS:
cd ios && pod install && cd ..

# For Android: auto-linked, no additional steps
```

**Expected result:** The messaging package is installed and native dependencies are linked on both platforms.

### 2. Configure iOS APNs for push notifications

iOS requires Apple Push Notification service (APNs) configuration for FCM to deliver notifications. Go to your Apple Developer account, create an APNs Authentication Key (p8 file) under Certificates, Identifiers & Profiles > Keys. Upload this key in Firebase Console > Project Settings > Cloud Messaging > Apple app configuration. In Xcode, enable Push Notifications and Background Modes (Remote notifications) capabilities for your app target.

```
// In Xcode:
// 1. Select your target > Signing & Capabilities
// 2. Click + Capability > Push Notifications
// 3. Click + Capability > Background Modes > check 'Remote notifications'

// In Firebase Console:
// 1. Project Settings > Cloud Messaging
// 2. Under Apple app configuration, click Upload
// 3. Upload your APNs Authentication Key (.p8 file)
// 4. Enter the Key ID and Team ID from Apple Developer portal
```

**Expected result:** Xcode shows Push Notifications and Remote notifications capabilities enabled. Firebase Console shows the APNs key uploaded.

### 3. Request notification permissions

On iOS, you must explicitly request permission from the user before showing notifications. On Android 13+, the POST_NOTIFICATIONS permission is also required. Call requestPermission() early in the app lifecycle, ideally during onboarding. Check the returned authorization status to determine if the user granted, denied, or has not decided.

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

async function requestNotificationPermission() {
  const authStatus = await messaging().requestPermission()
  const enabled =
    authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
    authStatus === messaging.AuthorizationStatus.PROVISIONAL

  if (enabled) {
    console.log('Notification permission granted:', authStatus)
  } else {
    console.log('Notification permission denied')
  }

  return enabled
}
```

**Expected result:** The permission dialog appears on iOS (and Android 13+), and the app receives the user's response.

### 4. Retrieve and manage the FCM token

The FCM token uniquely identifies this device for receiving push notifications. Retrieve it with getToken() and send it to your server to store alongside the user's account. Listen for token refreshes with onTokenRefresh() because tokens can change when the app is reinstalled or the user clears app data.

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

async function getFCMToken() {
  const token = await messaging().getToken()
  console.log('FCM Token:', token)

  // Send token to your backend
  await saveTokenToServer(token)
  return token
}

function listenForTokenRefresh() {
  return messaging().onTokenRefresh(async (newToken) => {
    console.log('Token refreshed:', newToken)
    await saveTokenToServer(newToken)
  })
}

async function saveTokenToServer(token: string) {
  // Save to Firestore, your API, etc.
  // await firestore().collection('users').doc(userId).update({ fcmToken: token })
}
```

**Expected result:** The FCM token is retrieved and stored on your server for sending targeted notifications.

### 5. Handle foreground messages

When the app is in the foreground, FCM delivers messages silently without showing a notification banner. Use onMessage() to listen for incoming messages and display them yourself, either with a custom in-app banner or by creating a local notification using a library like notifee. The handler receives a RemoteMessage object with notification and data properties.

```
import messaging from '@react-native-firebase/messaging'
import { useEffect } from 'react'

function useNotificationHandler() {
  useEffect(() => {
    const unsubscribe = messaging().onMessage(async (remoteMessage) => {
      console.log('Foreground message:', remoteMessage)

      // Option 1: Show in-app alert
      Alert.alert(
        remoteMessage.notification?.title || 'Notification',
        remoteMessage.notification?.body || ''
      )

      // Option 2: Use notifee for a proper notification banner
      // await notifee.displayNotification({
      //   title: remoteMessage.notification?.title,
      //   body: remoteMessage.notification?.body,
      // })
    })

    return unsubscribe
  }, [])
}
```

**Expected result:** Foreground messages trigger the onMessage callback and can be displayed to the user.

### 6. Handle background and quit-state messages

For messages received when the app is in the background or has been quit, register a background handler using setBackgroundMessageHandler(). This must be called outside of any React component, typically in your index.js file. On Android, FCM automatically displays a notification from the notification payload. On iOS, the background handler runs as a headless JS task.

```
// index.js (root of your React Native app)
import { AppRegistry } from 'react-native'
import messaging from '@react-native-firebase/messaging'
import App from './App'

// Register the background handler BEFORE AppRegistry.registerComponent
messaging().setBackgroundMessageHandler(async (remoteMessage) => {
  console.log('Background message:', remoteMessage)

  // Process the message (update badge, sync data, etc.)
  // Do NOT try to update UI state here
})

AppRegistry.registerComponent('MyApp', () => App)
```

**Expected result:** Background messages trigger the handler for data processing, and notification payloads automatically display a system notification.

### 7. Configure Android notification channels

Android 8.0+ requires notification channels for displaying notifications. Create a channel for your app's notifications with the appropriate importance level. Set the channel ID in your FCM message payload's android.notification.channel_id field to route notifications to the correct channel.

```
import notifee, { AndroidImportance } from '@notifee/react-native'

async function createNotificationChannel() {
  await notifee.createChannel({
    id: 'default',
    name: 'Default Notifications',
    importance: AndroidImportance.HIGH,
    vibration: true,
    sound: 'default',
  })
}

// Call this on app startup
createNotificationChannel()

// When sending from server, include the channel:
// admin.messaging().send({
//   token: userToken,
//   notification: { title: 'Hello', body: 'World' },
//   android: { notification: { channelId: 'default' } },
// })
```

**Expected result:** Notifications appear with the correct importance level and sound on Android devices.

## Complete code example

File: `notifications.ts`

```typescript
import messaging, {
  FirebaseMessagingTypes,
} from '@react-native-firebase/messaging'
import { Alert, Platform } from 'react-native'

export async function requestPermission(): Promise<boolean> {
  const authStatus = await messaging().requestPermission()
  return (
    authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
    authStatus === messaging.AuthorizationStatus.PROVISIONAL
  )
}

export async function getToken(): Promise<string> {
  if (!messaging().isDeviceRegisteredForRemoteMessages) {
    await messaging().registerDeviceForRemoteMessages()
  }
  return messaging().getToken()
}

export function onTokenRefresh(
  callback: (token: string) => void
): () => void {
  return messaging().onTokenRefresh(callback)
}

export function onForegroundMessage(
  callback: (msg: FirebaseMessagingTypes.RemoteMessage) => void
): () => void {
  return messaging().onMessage(callback)
}

export function setupForegroundAlert(): () => void {
  return messaging().onMessage(async (remoteMessage) => {
    Alert.alert(
      remoteMessage.notification?.title || 'New notification',
      remoteMessage.notification?.body || ''
    )
  })
}

export async function initializeNotifications(): Promise<void> {
  const permitted = await requestPermission()
  if (!permitted) {
    console.warn('Notification permission denied')
    return
  }

  const token = await getToken()
  console.log('FCM Token:', token)
  // TODO: Send token to your backend

  onTokenRefresh((newToken) => {
    console.log('Token refreshed:', newToken)
    // TODO: Update token on backend
  })
}
```

## Common mistakes

- **Not uploading the APNs key in Firebase Console, causing iOS notifications to silently fail** — undefined Fix: Go to Firebase Console > Project Settings > Cloud Messaging > Apple app configuration and upload your APNs Authentication Key (.p8 file) from Apple Developer portal.
- **Calling setBackgroundMessageHandler inside a React component instead of in index.js** — undefined Fix: Register the background handler at the top level of index.js, before AppRegistry.registerComponent. It must be available before any component mounts.
- **Not creating Android notification channels, causing notifications to be silently dropped on Android 8.0+** — undefined Fix: Create notification channels at app startup using notifee or the Android native API. Set the channel ID in your FCM payload.
- **Forgetting to enable Background Modes > Remote notifications in Xcode, preventing background delivery on iOS** — undefined Fix: In Xcode, select your target > Signing & Capabilities > add Background Modes and check 'Remote notifications'.

## Best practices

- Request notification permissions during onboarding with context about why notifications are useful, not immediately on app launch
- Store FCM tokens in your database keyed by user ID and update them on every token refresh
- Use the @notifee/react-native library alongside FCM for rich foreground notification display
- Register setBackgroundMessageHandler in index.js before AppRegistry.registerComponent
- Create Android notification channels at app startup with the appropriate importance level
- Use APNs Authentication Keys instead of certificates for iOS, as keys do not expire
- Handle both notification payloads and data-only payloads to support all notification types

## Frequently asked questions

### Why are my iOS notifications not arriving?

The most common cause is a missing APNs key in Firebase Console. Upload your p8 key under Project Settings > Cloud Messaging > Apple app configuration. Also verify Push Notifications and Background Modes capabilities are enabled in Xcode.

### How do I send a notification to a specific user?

Store the user's FCM token in your database when they grant permission. On your server, use the Firebase Admin SDK's messaging().send() with the user's token to deliver a targeted notification.

### What is the difference between notification and data messages?

Notification messages include a notification payload and are automatically displayed by the system when the app is in the background. Data messages contain only a data payload and are always delivered to your message handler, giving you full control over display.

### Can I send notifications from the Firebase Console without writing server code?

Yes, go to Firebase Console > Messaging > Create your first campaign. You can target by topic, segment, or specific FCM tokens. This is useful for marketing notifications but not for transactional or user-triggered notifications.

### How do I test push notifications on iOS Simulator?

The iOS Simulator supports push notification testing since Xcode 11.4. Create an APNs payload file and drag it onto the simulator. However, FCM-specific features like topics require a physical device.

### Can RapidDev help implement push notifications for a React Native app?

Yes, RapidDev's engineering team can set up FCM for React Native including platform-specific configuration, server-side notification logic, and in-app notification handling.

---

Source: https://www.rapidevelopers.com/firebase-tutorial/how-to-handle-fcm-in-react-native
© RapidDev — https://www.rapidevelopers.com/firebase-tutorial/how-to-handle-fcm-in-react-native
