Adding push notifications in React Native is more than just a feature—it’s the most direct line you have to your users. It turns your app from a one-way street into a dynamic conversation, using powerful services like Firebase Cloud Messaging (FCM) and the Apple Push Notification service (APNs).
Why Push Notifications Are a Must-Have for Your App
Before we jump into the setup, let's talk strategy. Think of push notifications as your secret weapon for bringing users back. Whether you're announcing a new feature, sharing a flash sale, or sending a critical alert, you're delivering value right to their lock screen.
Get this right, and you'll see a massive improvement in user retention, which is the lifeblood of any successful app.
The numbers don't lie. A well-thought-out notification plan can dramatically boost how often users return to your app. To put this in perspective, here's a look at how different sending frequencies can impact your retention rates based on industry data.
Notification Frequency vs User Retention Impact
| Notification Strategy | Average Retention Rate Increase |
|---|---|
| At least one notification | 120% |
| Weekly notifications | 440% |
| Daily notifications | 820% |
As you can see, consistent communication pays off. These figures, highlighted in a study on how notifications influence retention rates on rapidnative.com, show a clear correlation between thoughtful engagement and a loyal user base. The goal isn't just to send more, but to send smarter.
The Tech That Makes It Happen
So how does a message get from your server to a user's device? Your React Native app needs to talk to the native platform services. It really boils down to two key players.
Firebase Cloud Messaging (FCM): This is Google's workhorse for sending notifications. It’s the standard for Android and a popular, reliable choice for iOS and web too, making it a go-to for many React Native developers.
Apple Push Notification service (APNs): This is Apple's own system. It's built to send notifications securely and efficiently to iPhones, iPads, and other Apple devices. Your server pings APNs, and APNs handles the delivery.
Here's something that trips up a lot of developers: FCM doesn't actually deliver the notification to an iPhone itself. When you use FCM for iOS, it acts as a middleman. Your server sends the message to FCM, which then passes it along to APNs for the final delivery. Knowing this flow is a lifesaver when you're debugging.
Don’t Just Send—Measure
Firing off notifications into the void is a waste of time. You have to know what's working. Are users opening them? Are they completing the action you wanted them to take? Is your retention rate actually climbing?
This is where analytics come in. By tracking open rates and post-notification behavior, you can fine-tune your messaging. You start to learn what your users respond to, allowing you to send notifications they actually find helpful. This cycle of sending, measuring, and refining is what turns a noisy app into an essential one.
To get your measurement strategy right from the start, check out our deep dive into the best analytics tools for mobile apps. With this foundation, you’ll be ready to build a notification system that not only works technically but also drives real results.
Building Your Foundation with Firebase and APNs
Before a single line of JavaScript gets written, we have to lay the groundwork. Getting push notifications to work in a React Native app means first shaking hands with the native platform services. For Android, that’s Firebase Cloud Messaging (FCM), and for iOS, it's the Apple Push Notification service (APNs).
This is honestly where a lot of developers get tripped up. You're bouncing between different dashboards, generating keys, and trying to make sense of platform-specific jargon. Let's cut through that noise and get this native configuration sorted out, so you can get back to the fun part—building your app's UI and logic.
Setting Up Firebase Cloud Messaging
Your first stop is the Firebase Console. Think of Firebase as the central nervous system for your app's backend needs, and FCM is the part that handles sending messages to your Android users. It's also the middleman for your iOS notifications, which we'll get to.
Start by creating a new Firebase project or selecting one you already have. From there, you'll need to register your Android app with the project. This is a critical step.
- Provide Your Package Name: This has to be a perfect match for the
applicationIdin yourandroid/app/build.gradlefile. I've seen projects fail to connect for hours because of a simple typo here. - Download the Configuration File: Once registered, Firebase gives you a
google-services.jsonfile. This is basically your app's passport, containing all the credentials it needs to talk to Firebase. - Place the File Correctly: Drop this file directly into the
android/app/directory of your React Native project.
Once that file is in place, your Android app knows exactly how to securely identify itself to your Firebase project. No more configuration needed on the Android side for now.
Configuring the Apple Push Notification Service
Now for iOS. This process is a bit more involved because it requires a trip to the Apple Developer portal. APNs is the sole, secure gateway for all push notifications on Apple devices, and they're understandably strict about its configuration.
Don't just take my word for why this is important. Look at the numbers.

A 90% weekly open rate and a 65% daily increase in engagement aren't small figures. Getting your notification setup right is a direct line to keeping your users active and coming back.
To make it happen, head over to the "Certificates, Identifiers & Profiles" section of your Apple Developer account. You have a few key tasks here:
- Create an App ID: Every app needs a unique identifier. The crucial part is to explicitly enable the Push Notifications capability for this ID.
- Generate an APNs Auth Key: This is the modern way to do things. Forget the old, messy certificate-based approach. A single authentication key (
.p8file) works for all your apps, never expires, and is much easier to manage. - Download and Secure Your Key: After creating the key, you get one chance to download the
.p8file. Download it, and immediately store it somewhere safe like a team password manager. You'll also need to copy the Key ID and your Team ID.
Here's a pro-tip from experience: Always be mindful of your build environment. A classic rookie mistake is sending a test notification configured for production to a development build of your app. It will just fail silently, and you'll be left scratching your head. Your build type must always match the notification environment you're targeting.
With your new .p8 file, Key ID, and Team ID, it’s time to head back to the Firebase Console. Go into your project settings and find the "Cloud Messaging" tab. You'll see a spot to configure your Apple app. Upload your key, paste in the IDs, and you're set.
This final step creates the bridge between Firebase and Apple, allowing FCM to forward notifications to APNs, which then delivers them to your users' devices. If you want a more granular, step-by-step view, there's great supplemental info on integrating Firebase push notifications with a React Native iOS application on reactnativecoders.com.
Integrating Notifications Into Your React Native App

Alright, the native configuration for FCM and APNs is out of the way. Now for the fun part: getting everything wired up in your React Native code so you can actually send and receive notifications.
For most projects, the go-to library is Invertase's react-native-firebase. It’s a well-maintained, modular powerhouse that gives you direct JavaScript access to the native Firebase SDKs. We’ll start here, as it’s what you’ll encounter in the wild most often.
Installing and Configuring React Native Firebase
First things first, we need to get the core app and messaging modules into the project.
Just pop open your terminal and run this from your project's root:npm install @react-native-firebase/app @react-native-firebase/messaging
Once the packages are installed, you need to link the native bits. On iOS, it's usually a simple pod install away. Head into your ios folder and run it to connect the native dependencies in your Xcode project.
Android requires a couple of manual tweaks to its Gradle files. It's a quick, one-time setup:
- In your project-level
android/build.gradle, you need to tell Gradle where to find the Google Services plugin by adding its classpath.
// android/build.gradle
buildscript {
dependencies {
// … other dependencies
classpath 'com.google.gms:google-services:4.4.0'
}
} - Then, in your app-level
android/app/build.gradle, you apply the plugin, usually at the very bottom of the file. This step actually runs the plugin, which reads yourgoogle-services.jsonfile.
// android/app/build.gradle
// … other plugins
apply plugin: 'com.google.gms.google-services'
And that's it. Your app is now connected to the native Firebase SDKs.
The JavaScript Connection and Getting the FCM Token
With the libraries in place, we can finally write some code. The first order of business is asking the user for permission to send them notifications. If they agree, we can grab the unique FCM device token—this is the "address" you'll use from your backend to target a specific device.
I always recommend pulling this logic into a dedicated function or service file to keep your App.js clean. Here's a solid approach for handling permissions and fetching the token.
import messaging from '@react-native-firebase/messaging';
import { PermissionsAndroid, Platform } from 'react-native';
async function requestUserPermission() {
if (Platform.OS === 'ios') {
const authStatus = await messaging().requestPermission();
const enabled =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
if (enabled) {
console.log('Authorization status:', authStatus);
getFcmToken(); // We can fetch the token once permission is granted
}
} else if (Platform.OS === 'android') {
// For Android 13 (API 33) and higher, we need to request this permission
await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
// On Android, we can try to get the token regardless of permission status
getFcmToken();
}
}
async function getFcmToken() {
// Use await here to make sure you get the token before proceeding
const fcmToken = await messaging().getToken();
if (fcmToken) {
console.log("Your new FCM token is:", fcmToken);
// This is the token you'd send to your server to associate with the user
} else {
console.log("Failed to get FCM token. User may have denied permissions.");
}
}
You'll want to call requestUserPermission() early in your app's lifecycle, maybe from a useEffect hook in your main App component. We'll dig into the strategy behind when to ask for permission later on—it's more important than you'd think.
An Alternative: The Expo Push Notification Service
If you're building with the Expo framework, you have access to a beautifully simple alternative: Expo's own push notification service. It's a fantastic abstraction layer that smooths over the differences between APNs and FCM.
Here's why many developers love it:
- Unified Token: You get a single
ExpoPushTokenthat works for both iOS and Android. No more platform-specific token handling on your backend. - Painless Setup: In the Expo Go app, there's literally zero native setup. You just install
expo-notificationsand start coding. - Handy Server SDK: Expo offers a great little server SDK (
expo-server-sdk-node) that makes sending notifications from your backend a breeze.
The main trade-off? Your notifications pass through Expo's servers. For 99% of apps, this is a non-issue and works perfectly. But if you have strict data residency or compliance needs, sticking with react-native-firebase gives you that direct connection to Google and Apple.
Can I use this outside of Expo Go?
Absolutely. You can use theexpo-notificationspackage in any bare React Native project. You'll still need to do the native APNs/FCM setup yourself, but you get to use Expo's simplified JavaScript API and unified token system, which is a great compromise.
Handling Incoming Messages with Notifee
Here’s a crucial detail: when your app is in the foreground, react-native-firebase delivers the notification payload to your app, but it won't automatically show a visible alert to the user. This is by design. It gives you the power to handle the notification silently or display a custom in-app banner instead of interrupting the user with a system notification.
To actually show a standard notification while the app is open, you need a local notifications library. The undisputed champion in this space is Notifee, also made by Invertase.
Notifee and react-native-firebase are designed to work together. You simply listen for a new message from Firebase and then use Notifee to build and display a local notification on the device.
Here's how you tie them together to handle those foreground messages:
import messaging from '@react-native-firebase/messaging';
import notifee, { AndroidImportance } from '@notifee/react-native';
messaging().onMessage(async remoteMessage => {
console.log('A new FCM message arrived in the foreground!', remoteMessage);
// On Android, we need to create a channel for the notification
const channelId = await notifee.createChannel({
id: 'default',
name: 'Default Channel',
importance: AndroidImportance.HIGH, // High importance for heads-up notification
});
// Now, display the notification using Notifee
await notifee.displayNotification({
title: remoteMessage.notification.title,
body: remoteMessage.notification.body,
android: {
channelId,
smallIcon: 'ic_launcher', // Make sure this is a valid drawable resource
pressAction: {
id: 'default',
},
},
});
});
This combo gives you a complete, professional setup. react-native-firebase handles the heavy lifting of connecting to the push services, while Notifee provides the tools to create rich, interactive notifications right on the user's device.
Mastering Permissions and Background Message Handling
Getting notifications to fire is one thing. Building a system that users actually appreciate is another challenge entirely. This is where we move beyond the basics of sending push notifications in React Native and start thinking about the user experience. It’s about asking for permission at the right time and making sure important messages get through, even when your app isn’t active.

Frankly, this is the part of the process that separates a decent app from a truly great one. Let's get into the details of permissions and background message handling.
The Art of the Permission Request
The absolute worst time to ask for notification permissions? The second a user opens your app for the first time. They don't know you, they don't trust you, and they have zero context for why you need them. A cold, immediate prompt is a surefire way to get a "Don't Allow," and with iOS, you don't get a do-over.
A much smarter strategy is to wait for the perfect moment—a point where the user clearly understands the value they'll get in return.
- For a social app: Don't ask until they get a friend request or a direct message. The value is obvious.
- In an e-commerce app: Pop the question after they place an order, framing it as a way to get shipping updates.
- With a productivity app: Wait until they create a task with a deadline, then offer to remind them.
To pull this off, you'll need what's called a "pre-permission" dialog. This is a custom UI you build yourself that explains why you're about to ask for permission. If the user taps "Okay," then you trigger the official system prompt with messaging().requestPermission(). If they say no, you've respected their choice without burning your single shot.
Handling Messages When Your App Is Closed
Reliably processing notifications when your app is in the background—or worse, terminated—is a non-negotiable feature for many apps. Think about chat alerts, breaking news, or even silent data syncs. react-native-firebase gives us a specific tool for this job: the background message handler.
This handler is a bit special. You have to register it at the very top level of your app's entry point, which is usually index.js, and it must be done before you register your main App component.
// index.js
import { AppRegistry } from 'react-native';
import messaging from '@react-native-firebase/messaging';
import App from './App';
import { name as appName } from './app.json';
// Register background handler
messaging().setBackgroundMessageHandler(async remoteMessage => {
console.log('Message handled in the background!', remoteMessage);
// You could trigger a background data sync or other headless tasks here.
});
AppRegistry.registerComponent(appName, () => App);
The key thing to remember is that your background handler runs in a "headless" JavaScript environment, completely separate from your app's UI. You can't update component state or touch the UI from here. It's built for quick, background-only tasks. To get a better handle on this concept, check out our guide on how to know if a React Native app goes to the background.
Creating Android Notification Channels
Ever since Android 8.0 (API level 26), all notifications must belong to a channel. There's no way around it. This is actually a great feature for users, as it gives them granular control to, for instance, turn off promotional notifications while keeping critical alerts enabled.
You should create your channels as soon as your app starts, and the best library for managing this is, without a doubt, Notifee.
import notifee, { AndroidImportance } from '@notifee/react-native';
export async function createNotificationChannels() {
// Create a "default" channel for general stuff
await notifee.createChannel({
id: 'default',
name: 'General Notifications',
importance: AndroidImportance.HIGH,
});
// Create a separate "chat" channel for DMs
await notifee.createChannel({
id: 'chat',
name: 'Direct Messages',
importance: AndroidImportance.HIGH,
sound: 'default', // You can even set custom sounds per channel
});
}
After setting them up, you just reference the channelId when you display a notification, and Android takes care of the rest. By putting in the effort to master permissions, background tasks, and channels, your push notifications in React Native will feel less like an interruption and more like a genuinely helpful, well-integrated feature of your app.
Advanced Strategies and Best Practices for 2026
Alright, you've got the basics wired up and notifications are hitting your test devices. Now it's time for the fun part: making them actually good. This is where we move from a simple, functional setup to a notification experience that users appreciate instead of silence.
The difference between a pro-level app and an amateur one often comes down to the details of its notification strategy. We’re talking about going beyond just sending a line of text. We'll get into creating rich, interactive alerts that feel like a core part of your app, not just an afterthought.
Go Beyond Basic Alerts with Rich Notifications
Think about it: a generic "New Message" alert gets the job done, but it's boring. What if, instead, the notification showed the sender's profile picture and gave you "Reply" and "Mark as Read" buttons right on the lock screen? That's a much better experience. This is what rich notifications are all about.
With a library like Notifee, you can easily attach images, add quick action buttons, and customize how your notifications look. For an e-commerce app, this could mean showing a product image in a "back in stock" alert. That little bit of visual context can make a huge difference in whether someone taps through.
Implement Deep Linking for Seamless Navigation
Deep linking is a non-negotiable for any serious app. When a user taps a notification, they should land exactly where they expect to go. If you send an alert about a sale on shoes, tapping it should take them straight to the shoes page, not the app’s home screen. Making them hunt for the content you just told them about is a surefire way to frustrate them.
Setting this up involves defining a URL scheme for your app (like myapp://) and then passing a deepLink in your notification's data payload. Your navigation library, such as React Navigation, can then be configured to catch these incoming links and take the user to the right screen.
// A sample data payload with a deep link
{
"to": "",
"notification": {
"title": "Your Order #12345 Has Shipped!",
"body": "Tap to see tracking details."
},
"data": {
"deepLink": "myapp://orders/12345"
}
}
This one feature alone turns your notifications from simple pop-ups into powerful shortcuts that make your app feel way more polished and intuitive.
Embrace Personalization and User Segmentation
One of the worst mistakes I see developers make is blasting the same generic message to their entire user base. That's the fastest way to get users to turn off your notifications for good. The industry standard has shifted completely toward personalization.
Instead of shouting at everyone, you need to segment users into groups based on their behavior, preferences, or even location. This lets you send messages that are genuinely relevant.
- Behavioral: Target users who haven't opened the app in 7 days with a special "we miss you!" offer.
- Interests: If a user frequently looks at a certain product category, let them know when new items drop.
- Location: Alert users in a specific city about a local event or promotion they might actually care about.
This targeted approach ties into a core best practice: don't ask for permission on a cold open. Show the user why your app is valuable first, then ask for notification permissions. As you can read in these industry-standard recommendations for React Native push notifications, this simple timing change can dramatically reduce opt-outs.
A great notification strategy is a constant feedback loop. Test your message copy and images. Measure your open rates and conversions. Then, refine your segments and timing based on what the data tells you. This cycle is what separates apps that grow from those that get uninstalled.
Common Push Notification Questions and Fixes
Even after a seemingly perfect setup, push notifications can be tricky. A lot of things have to line up just right across Apple, Google, and your own codebase. Let's walk through some of the most common questions and sticking points I've seen developers run into over the years.
Why Aren't My Push Notifications Working on iOS?
This one trips up almost everyone at some point, and it’s almost always an issue with your APNs configuration.
First, pop open Xcode and make sure the Push Notifications capability is actually enabled for your app's target. If that looks good, head over to your Apple Developer portal. You need to double-check that your App ID has the push notification service enabled for both development and production environments.
A classic mistake is a mismatch between your build type and the APNs server. You simply can't send a notification from a production certificate to a development build of your app (and vice-versa). Lastly, confirm that you're using the right APNs Auth Key (.p8 file) on your server and that you’ve uploaded that same key to your Firebase project.
What's the Real Difference Between Notification and Data Messages?
Getting this distinction right is fundamental. A Notification message is the simple, "display-only" type. When your app is in the background, the operating system grabs it and displays it automatically using a standard title and body. You have very little say in what happens.
A Data message, however, is a silent payload of key-value pairs that you define. It always gets delivered directly to your app's code, whether the app is in the foreground or background. This gives you total control to handle it however you want—maybe to refresh some data silently or trigger some custom UI.
My favorite approach is to send a hybrid message. By crafting a payload with both a
notificationand adatablock, you get the best of both worlds. The OS guarantees the user sees a notification, and when they tap it, your app's code gets the data payload to handle the interaction intelligently—like deep-linking them to just the right screen.
How Can I Test Notifications Without Annoying My Users?
You should absolutely never test new notification ideas on your live production app. The quickest and safest way to test is right in the Firebase Console. Just use the "Send test message" feature, paste in your device's specific FCM token, and you can send a notification directly to yourself.
For more organized testing with your team, here's a great strategy:
- Create a dedicated Firebase topic, something like
internal_testers. - Inside a debug menu in your app, add a button that subscribes the device to that topic.
- Now you can target notifications to the
internal_testerstopic, hitting only your team's devices and leaving your actual users alone.
If you need to get more technical, I often use tools like Postman or a simple cURL command to hit the FCM API directly. This lets you really tinker with the raw JSON payload and test different data structures and deep link formats without needing any UI.
Can I Use Expo's Notification Library in a Bare React Native Project?
Yes, and it can be a really smart move. You can absolutely install the expo and expo-notifications packages into a bare React Native project. This gives you access to Expo's fantastic, simplified JavaScript API and its unified ExpoPushToken system, which can make your backend code a whole lot cleaner.
Just be aware that this doesn't let you skip the native grunt work. You are still on the hook for doing the full Firebase and APNs configuration in your native iOS and Android projects. Think of it as a middle ground: you get the power and flexibility of the bare workflow combined with the developer-friendly API that makes Expo so popular.
At React Native Coders, we're all about providing deep, practical guides to help you build outstanding mobile apps. For more tutorials and expert analysis, come check us out at https://reactnativecoders.com.





















Add Comment