Push notifications are one of the most powerful tools for mobile apps.
A 2025 study shows that apps using push notifications can have up to 3 times higher user engagement and significantly better retention rates. For React Native apps on Android, the industry standard for this is Google’s Firebase Cloud Messaging (FCM).
It’s powerful and free, but the initial setup can be complex.
This guide provides a clear, step-by-step tutorial for US developers. We’ll walk you through the entire process, explain the key differences between notification types, and show you how to get robust push notifications working in your app.
Table of Contents
Architectural and Functional Overview of Firebase Cloud Messaging (FCM)
In September 2025, if you need to send push notifications to your mobile or web app, Firebase Cloud Messaging (FCM) is the industry-standard solution. It’s a powerful, cross-platform service from Google that lets you reliably deliver messages to your users, completely free of charge. Let’s break down how it works.
How FCM Works: A Two-Part System
FCM isn’t just a library you install in your app; it’s a complete system with two main parts:
- Your Server (The Sender): This is where you create and send messages from. It can be your own backend or a cloud function. You use the Firebase Admin SDK to tell FCM who to send a message to and what it should say.
- Your App (The Client): This is your mobile or web app where users receive the messages. The FCM SDK in your app handles the complex work of getting a unique token for the device and listening for incoming notifications.
For testing, the Firebase Console also has a tool that lets you send notifications without writing any server code, which is great for marketing campaigns and development.
FCM for React Native: The Best of Both Worlds
For React Native developers, the official @react-native-firebase library acts as a “bridge.” It connects your JavaScript code directly to the native Firebase SDKs on both iOS and Android.
This is a huge advantage because it gives your app the full performance and power of the native platform, including efficient background message handling that’s difficult to do with pure JavaScript alone.
What You Can Do With FCM: Key Features
The combination of FCM’s backend and the client SDK gives you a powerful set of tools for user engagement.
- Flexible Targeting: You have several ways to target your users. You can send a message to a single device using its unique token, or you can broadcast a message to thousands of users at once by having them subscribe to Topics (like breaking_news or new_promotions).
- It’s Completely Free: This is a huge deal. You can send as many messages as you want, to as many users as you have, and it costs you nothing. It’s one of the most cost-effective engagement tools available.
- Powerful Analytics: When you connect FCM to Google Analytics, you get a detailed dashboard in the Firebase Console. You can track how many of your messages are being sent, delivered, and opened, giving you critical insights into your user engagement campaigns.
FCM Messages: Choosing Between Automatic and Full Control
When sending a push notification with Firebase Cloud Messaging in September 2025, the most important question to ask is: “How much control do I need?” Your answer will determine whether you should use a “Notification” message or a “Data-only” message. This choice completely changes who handles the message—the operating system or your app—especially when your app is closed.
The Automatic Path: Notification Messages (Easy Mode)
A Notification message is for simple alerts. You give FCM a title and a body, and the system does the rest. When your app is in the background, the OS will automatically display a standard notification for you. This is the easiest way to send a basic alert, but the trade-off is that your app’s code doesn’t run in the background.
The Full Control Path: Data-Only Messages (Pro Mode)
A Data-only message gives you complete control. You send a custom payload of data, and your app is 100% responsible for handling it. When a data message arrives in the background, it wakes up your app’s background message handler. This allows you to run custom code, like syncing data, downloading an image, or creating a rich, fully customized notification.
A critical pro-tip: To make sure these messages arrive promptly in the background on Android, you must send them from your server with the priority set to high.
The Golden Rule: It All Depends on Your App’s State
The way messages are handled changes completely depending on whether your app is open or closed.
- When your app is in the FOREGROUND (on-screen): It’s simple. Both Notification and Data-only messages are delivered to your running app’s onMessage listener. The OS never automatically shows a notification. You are always in control.
- When your app is in the BACKGROUND or QUIT: This is where the paths split. Notification messages are handled by the OS. Data-only messages are handled by your app’s background code.
- A final warning on hybrid messages (Notification + Data): If you send a hybrid message to a backgrounded app, the OS will automatically display the notification, and your background code will not run. The data is only available if the user taps the notification.
Table 1: Message Handling Behavior on Android
The following table summarizes the behavior of different FCM message types based on the application’s state. This serves as a critical reference for architects and developers to ensure their implementation aligns with the expected system behavior.
Message Type | App State | OS/FCM SDK Behavior | React Native Handler Triggered |
Notification-Only | Foreground | Message is not displayed. | onMessage() |
Background | Displays a notification in the system tray. | None | |
Quit | Displays a notification in the system tray. | None | |
Data-Only | Foreground | No automatic action. | onMessage() |
Background | No automatic action. | setBackgroundMessageHandler() | |
Quit | No automatic action. | setBackgroundMessageHandler() | |
Hybrid (Notification + Data) | Foreground | Message is not displayed. | onMessage() (contains both payloads) |
Background | Displays notification from notification payload. | None (data is available on tap) | |
Quit | Displays notification from notification payload. | None (data is available on tap) |
A Comprehensive Implementation Guide for Android
This section provides a sequential, phased guide to integrating Firebase Cloud Messaging into a React Native project with a specific focus on the Android platform. Following these steps in order will mitigate common setup errors.
Phase 1: Firebase Project and App Configuration
The first phase involves setting up the necessary cloud infrastructure on the Firebase platform.
- Create a Firebase Project: Navigate to the Firebase Console and create a new project. This process involves providing a project name and optionally linking a Google Analytics account, which is highly recommended for accessing the FCM reporting dashboard.
- Register Your Android App: Within the project dashboard, add a new Android application. This requires two key pieces of information:
- Package Name: This must exactly match the applicationId found in your project’s android/app/build.gradle file.
- Debug Signing Certificate (SHA-1): This is optional but recommended for certain Firebase services. It can be generated by running a keytool command against the debug.keystore file located in your project’s android/app directory.
- Download Configuration File: Upon registering the app, Firebase will generate a google-services.json file. This file contains all the necessary project keys and identifiers for the client SDK to connect to your Firebase project. Download this file and place it in the root of your Android app module, specifically at android/app/google-services.json.
Phase 2: React Native Environment Setup
With the Firebase project configured, the next step is to add the required libraries to the React Native project.
- Install Core App Module: The core @react-native-firebase/app module is a prerequisite for all other Firebase services. Install it using Yarn or npm:
yarn add @react-native-firebase/app - Install Messaging Module: Install the Cloud Messaging module:
yarn add @react-native-firebase/messaging
Phase 3: Native Android Project Configuration
This phase involves modifying the native Android project files to integrate the Firebase SDK. This is a critical step where many configuration errors occur.
- Configure Project-Level build.gradle: Open the android/build.gradle file and add the Google services plugin to the dependencies block within the buildscript section:
Groovy
buildscript {
//…
dependencies {
//…
classpath ‘com.google.gms:google-services:4.4.3’ // Use the latest version
}
} - Configure App-Level build.gradle: Open the android/app/build.gradle file and apply the Google services plugin at the very top of the file 3:
Groovy
apply plugin: ‘com.android.application’
apply plugin: ‘com.google.gms.google-services’ // Add this line - Synchronize Gradle: After saving the changes, the IDE (like Android Studio) or the command line build process will need to sync the Gradle files to download and link the new dependencies.
Phase 4: Modern Permission Handling (Android 13+)
Android 13 (API level 33) introduced a runtime permission for posting notifications, which requires explicit handling in the application code.
- Declare Permission in Manifest: First, declare the POST_NOTIFICATIONS permission in the android/app/src/main/AndroidManifest.xml file:
XML
<manifest…>
<uses-permission android:name=”android.permission.POST_NOTIFICATIONS”/>
<application…>
…
</application>
</manifest> - Request Permission at Runtime: The react-native-firebase documentation recommends using React Native’s built-in PermissionsAndroid API to request this permission from the user. This check should be performed at an appropriate point in the user flow, such as during onboarding, to provide context for why notifications are needed.
JavaScript
import { PermissionsAndroid, Platform } from ‘react-native’;
async function requestUserPermission() {
if (Platform.OS === ‘android’) {
try {
// For API level 33+
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
{
title: ‘Notification Permission’,
message: ‘This app needs access to send you notifications.’,
buttonNeutral: ‘Ask Me Later’,
buttonNegative: ‘Cancel’,
buttonPositive: ‘OK’,
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log(‘You can use the notifications’);
} else {
console.log(‘Notification permission denied’);
}
} catch (err) {
console.warn(err);
}
}
}
Phase 5: FCM Token Management
Each device needs a unique token to receive targeted messages.
- Retrieve the Token: Use the messaging().getToken() method to retrieve the device’s unique FCM registration token. This is an asynchronous operation that returns a promise with the token string.
- Store the Token: This token should be sent to and stored on your application server, typically associated with a user’s account. This allows the backend to send targeted notifications to that specific user/device combination.
- Monitor for Refreshes: The FCM token can change under certain circumstances (e.g., app reinstall, user clears app data).7 It is crucial to listen for these changes using the
messaging().onTokenRefresh() listener. When a new token is generated, this listener will fire, and the new token should be sent to the server to replace the old one.
Phase 6: Implementing Message Listeners
With the setup complete, the final step is to implement the JavaScript handlers to process incoming messages.
- Foreground Messages: To handle messages when the app is open and active, use the messaging().onMessage() listener. This listener should be set up inside a React component (e.g., within a useEffect hook) so that it has access to the app’s state and can trigger UI updates.
JavaScript
useEffect(() => {
const unsubscribe = messaging().onMessage(async remoteMessage => {
Alert.alert(‘A new FCM message arrived!’, JSON.stringify(remoteMessage));
});
return unsubscribe;
},); - Background & Quit State Messages: To handle data-only messages when the app is in the background or has been terminated, use messaging().setBackgroundMessageHandler(). This function must be registered at the top level of your index.js file, outside of any React component lifecycle, as it needs to be accessible without the app’s UI being mounted.
JavaScript
// index.js
import messaging from ‘@react-native-firebase/messaging’;
messaging().setBackgroundMessageHandler(async remoteMessage => {
console.log(‘Message handled in the background!’, remoteMessage);
}); - Handling User Interaction: To detect when a user taps a notification to open the app, two methods are provided:
- messaging().getInitialNotification(): This method should be called when the app starts. It returns a promise that resolves with the notification that launched the app from a quit state, or null if it was opened normally.
- messaging().onNotificationOpenedApp(): This sets up a listener that is triggered when the app is already in the background and is brought to the foreground by a user tapping a notification.
Phase 7: Validation via the Firebase Notifications Composer
To confirm that the entire pipeline is working correctly, use the Firebase Console:
- Navigate to the “Cloud Messaging” section.
- Click “Create your first campaign” or “New campaign” and select “Notifications.”
- Enter a title and body for the test message.
- In the right-hand panel, click “Send test message.”
- Paste the device token obtained in Phase 5 into the provided field.
- Click “Test.” The notification should appear on the target device.

Advanced Topics and Strategic Customization
Once you have the basics of Firebase Cloud Messaging (FCM) working, it’s time to build a truly professional notification experience. In September 2025, this means handling server-side sending, deep-linking, and creating rich, interactive notifications. Let’s look at the key advanced topics you’ll need to master.
Sending Messages from Your Server
In a real-world app, you’ll send notifications from your own backend, not the Firebase Console. You do this with the Firebase Admin SDK. The most important thing to remember is when you’re sending a data-only message to an Android device, you must set the priority to high in your server-side code. This ensures your background handler will be triggered promptly to process the message.
Deep-Linking: Navigating Users to the Right Screen
A common goal is to send a user to a specific screen in your app when they tap a notification. This is called deep-linking. The process is simple: include the destination screen (e.g., ‘Profile’) in the data payload of your FCM message. Then, in your app, use the getInitialNotification() and onNotificationOpenedApp() listeners to read that data and tell your navigation library, like React Navigation, to go to the correct screen.
Customizing the Default Notification Look
When the Android OS automatically displays a notification for you (when your app is in the background), it has a default, generic icon and color. You can customize this to match your brand by adding a couple of simple <meta-data> tags to your AndroidManifest.xml file. This is an easy way to make your system notifications look more polished and professional.
The Pro’s Toolkit: Level Up with Notifee
The default Firebase library is great for receiving messages, but it’s very basic when it comes to displaying them. For a truly modern and rich notification experience, you need a dedicated local notifications library. The officially recommended tool for this is Notifee.
Think of them as a team: @react-native-firebase gets the message, and Notifee displays it. This separation allows for powerful features that the default library can’t do, such as:
- Displaying notifications when your app is in the foreground.
- Creating rich notifications with images, progress bars, and action buttons.
- Managing Android Notification Channels, which are mandatory for modern Android versions.
In 2025, the combination of @react-native-firebase/messaging and Notifee is the standard stack for building a professional notification system in React Native.
An Objective Analysis of Strengths and Weaknesses
Firebase Cloud Messaging (FCM) is an incredibly powerful tool for sending push notifications in a React Native app, but it’s not without its challenges. In September 2025, making an informed decision means understanding both the good and the bad. Here’s a balanced look at the pros and cons.
The Good: Why FCM is the Industry Standard
- It’s Reliable and Scalable: FCM is built on Google’s infrastructure, so it’s designed to be incredibly reliable and can handle sending billions of messages a day.
- It’s Completely Free: This is a massive advantage. You can send an unlimited number of notifications at no cost, which is a huge win for any business, from a small startup to a large enterprise.
- Powerful Targeting and Analytics: It gives you sophisticated ways to target specific groups of users and provides a detailed dashboard in the Firebase Console to track your delivery and open rates.
- True Native Power: The react-native-firebase library lets your app tap into the full power of the native device, most importantly the ability to run custom code in the background when a data-only message is received.
The Bad: The Trade-Offs and Frustrations
- The Setup is Complicated: The biggest hurdle is the initial setup. It forces you to leave the comfort of JavaScript and edit native Android files like build.gradle and AndroidManifest.xml. This can be intimidating and is a common place where developers get stuck.
- You Get Locked In: Once you’re deeply integrated with the Firebase ecosystem, it’s a major engineering effort to switch to a different provider later on. It’s a significant long-term commitment.
- Debugging Can Be a Nightmare: When a notification doesn’t arrive, it can feel like a “black box.” The cause is often unclear—it could be anything from an expired token to aggressive battery-saving settings on a user’s Android phone.
- The Message Handling Rules are Tricky: The difference in how “Notification” and “Data-only” messages are handled when your app is in the background is complex, non-intuitive, and a frequent source of bugs for new developers.
Review Summary: A Synthesis of Community Sentiment
So, what’s it really like to work with Firebase Cloud Messaging in a React Native app? In September 2025, the discussions on forums like Reddit and Quora paint a very clear picture: it’s an incredibly powerful tool that can also be incredibly frustrating to set up. Let’s look at the common pain points and praises from the developer community.
The Common Pain Points and Frustrations
- The “Initial Setup Hell”: This is the number one complaint. A huge number of developers report struggling with the native Android configuration. Getting all the Gradle dependencies and versions to line up correctly can be a painful, time-consuming process.
- Unreliable Background Handler on Some Phones: A particularly frustrating issue is that the background handler for data-only messages can be unreliable on certain Android devices. Aggressive battery-saving features on phones from manufacturers like Xiaomi or Huawei can silently kill your background process, preventing your code from running.
- Debugging is a Guessing Game: When a notification doesn’t arrive, it often feels like a “black box.” The lack of clear, client-side error messages makes troubleshooting a frustrating process of elimination.
The Praises: Why It’s Still the Top Choice
Despite the challenges, developers stick with it for a few very important reasons.
- It “Just Works” (Once You Get It Working): The prevailing sentiment is that while the setup can be a nightmare, once it’s correctly configured, FCM is exceptionally reliable and performant, even when sending messages at a massive scale.
- Background Processing is a Superpower: Experienced developers love the ability of data-only messages to trigger code in the background. It’s a powerful feature for building sophisticated apps that can sync data silently and provide a more real-time user experience.
- The “Firebase + Notifee” Stack is King: The community has a clear recommendation for building the best notification system. The combination of @react-native-firebase/messaging for receiving messages and Notifee for displaying rich, interactive notifications is consistently praised as the definitive, most powerful stack for React Native.
So, after all the deep dives and technical details, which push notification strategy is right for your React Native app in September 2025? The best choice depends on your project’s needs, your team’s skills, and your budget. Here are our final recommendations to help you pick the right path.
The Best Choice for Power and Control: @react-native-firebase
The official @react-native-firebase/messaging library is the most powerful and flexible option. It’s the best choice if:
- You’re already using other Firebase services like Firestore or Authentication.
- Your app needs to perform complex background tasks, like syncing data when a notification arrives.
- Your team is comfortable with touching a bit of native Android configuration.
- You need a free, highly scalable solution that can handle millions of users.
Strategic Alternatives for Simplicity or More Features
But what if you don’t need all that power, or you want an even easier setup? There are two great alternatives.
For Maximum Simplicity: Expo Push Notifications
If your top priority is simplicity and speed, and you’re building your app with the Expo framework, then Expo Push Notifications is the ideal choice. It handles all the complex native setup for you, so you can work entirely in JavaScript. The trade-off is that you get less control and lose some advanced features, like robust background processing.
For More Features (with a Price): OneSignal
Services like OneSignal offer a great middle ground. They often use FCM’s powerful infrastructure under the hood but provide a much more developer-friendly interface on top. For a paid subscription, you often get extra features like advanced analytics dashboards, A/B testing tools, and easier user segmentation. This is a great option for teams that want more features than Expo but want to avoid the native setup of react-native-firebase.
Concluding Assessment
Firebase push notifications offer great power and control for React Native apps. The @react-native-firebase/messaging library is the standard for teams that need full control over the entire notification process, from background data to user actions.
A successful setup depends on one key concept: understanding the difference between notification messages and data-only messages. Teams that master this get a powerful tool to build engaging, real-time experiences for their Android users.
Ready to add push notifications to your app? Start by outlining a clear strategy for when to use notification messages versus data-only messages.