React Native Expo Push Notifications with FCM: A Step-by-Step Guide

Andria shonia
7 min readApr 17, 2023

--

In this blog post, I will share the different scenarios I encountered while setting up push notifications in React Native Expo with FCM (Firebase Cloud Messaging) on iOS and Android.

Note: Push notifications are not supported while using Expo Go, they only work on physical/real devices. Rest assured, I will guide you through the process and provide you with detailed instructions on how to use them.

Steps

  1. Set up Firebase project and add apps (iOS and Android) — https://console.firebase.google.com
  2. Install React Native Firebase package — https://rnfirebase.io/
  3. Create a development build— https://docs.expo.dev/
  4. Send notifications

1. Firebase project setup

Setting up a Firebase project is necessary for FCM push notifications because it provides the authentication, device registration, and notification configuration needed to send push notifications to devices running your app.

  1. Go to https://console.firebase.google.com
  2. click create project
  3. Enter project name
  4. click continue and you will be redirected to project page.
Image from google firebase

Create iOS and Android apps

iOS

  1. To add your iOS app to Firebase, click on the iOS button in the Firebase console (you should see a screen similar to the one above).
  2. Enter your app’s bundle ID in the corresponding field. You can find your bundle ID in your project’s app.json or app.config.js file.
  3. Download the GoogleService-Info.plist file from Firebase and add it to the root directory of your project.

Android

  1. To add your Android app to Firebase, click on the Android button in the Firebase console.
  2. Enter your app’s bundle ID in the corresponding field. You can find your bundle ID in your project’s app.json or app.config.js file.
  3. Download the google-services.json file from Firebase and add it to the root directory of your project.

Downloaded files should be included in your app.json or app.config.js

{
"expo": {
// ... other config properties
"ios": {
"bundleIdentifier": "your.app.bundle.identifier",
"googleServicesFile": "./GoogleService-Info.plist"
}
"android": {
"package": "your.app.package.name",
"googleServicesFile": "./google-services.json"
}
}
}

If you are going to send notifications from your server make sure that you have enabled Cloud Messaging in your Firebase project’s settings, as shown in the screenshot.

Project settings firebase

2. React Native Firebase package

Install the React Native Firebase app and messaging module to the root of your React Native project with NPM or Yarn:

# Install & setup the app module

npm install --save @react-native-firebase/app
or
yarn add @react-native-firebase/app


# Install the messaging module

npm install @react-native-firebase/messaging
or
yarn add @react-native-firebase/messaging

Once install that packages, you'll need to add the config plugin to the plugins array in either your app.json or app.config.js file.

{
"expo": {
// ... other config properties
"ios": {
"bundleIdentifier": "your.app.bundle.identifier",
"googleServicesFile": "./GoogleService-Info.plist"
}
"android": {
"package": "your.app.package.name",
"googleServicesFile": "./google-services.json"
}
"plugins": [
"@react-native-firebase/app",
]
}
}

Bellow in snippet of code useEffect hook sets up various event listeners to handle notifications in different states of the app (e.g. in the foreground, background, or completely quit). The hook also schedules notifications to be shown at the appropriate times. The return function cleans up the event listeners when the component is unmounted.

const requestUserPermission = async () => {
const authStatus = await messaging().requestPermission();
const enabled =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;

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

useEffect(() => {

if (requestUserPermission()) {
messaging()
.getToken()
.then(
token => console.log(token)
);
}
}, []);

// Set up the notification handler for the app
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: false,
}),
});

// Handle user clicking on a notification and open the screen
const handleNotificationClick = async (response) => {
const screen = response?.notification?.request?.content?.data?.screen;
if (screen !== null) {
navigation.navigate(screen);
}
};

// Listen for user clicking on a notification
const notificationClickSubscription =
Notifications.addNotificationResponseReceivedListener(
handleNotificationClick
);

// Handle user opening the app from a notification (when the app is in the background)
messaging().onNotificationOpenedApp((remoteMessage) => {
console.log(
"Notification caused app to open from background state:",
remoteMessage.data.screen,
navigation
);
if (remoteMessage?.data?.screen) {
navigation.navigate(`${remoteMessage.data.screen}`);
}
});

// Check if the app was opened from a notification (when the app was completely quit)
messaging()
.getInitialNotification()
.then((remoteMessage) => {
if (remoteMessage) {
console.log(
"Notification caused app to open from quit state:",
remoteMessage.notification
);
if (remoteMessage?.data?.screen) {
navigation.navigate(`${remoteMessage.data.screen}`);
}
}
});

// Handle push notifications when the app is in the background
messaging().setBackgroundMessageHandler(async (remoteMessage) => {
console.log("Message handled in the background!", remoteMessage);
const notification = {
title: remoteMessage.notification.title,
body: remoteMessage.notification.body,
data: remoteMessage.data, // optional data payload
};

// Schedule the notification with a null trigger to show immediately
await Notifications.scheduleNotificationAsync({
content: notification,
trigger: null,
});
});

// Handle push notifications when the app is in the foreground
const handlePushNotification = async (remoteMessage) => {
const notification = {
title: remoteMessage.notification.title,
body: remoteMessage.notification.body,
data: remoteMessage.data, // optional data payload
};

// Schedule the notification with a null trigger to show immediately
await Notifications.scheduleNotificationAsync({
content: notification,
trigger: null,
});
};

// Listen for push notifications when the app is in the foreground
const unsubscribe = messaging().onMessage(handlePushNotification);

// Clean up the event listeners
return () => {
unsubscribe();
notificationClickSubscription.remove();
};
}, []);

Development builds

Its time to create development build to test notifications. Reminder — Push notifications are working only on real devices.

iOS

  1. If you don’t have Expo account, create — https://expo.dev/
  2. Run eas login
  3. Run command — eas build --profile development --platform ios
  4. Follow instructions and finaly build will start
  5. Once the build is complete, a QR code and URL to your build will appear in the terminal. Your build will be stored on Expo.

Apple Developer membership is required to create and install a development build on an iOS device.

To register any iOS device you’d like to develop onto your ad hoc provisioning profile, run the following command eas device:create and follow the on-screen instructions to complete the device creation process.

Process of installing device

After the app has been installed on your device, run the command npx expo start --dev-client and open the app.

Android

  1. If you don’t have Expo account, create — https://expo.dev/
  2. Run eas login
  3. Run command — eas build --profile development --platform android
  4. Follow instructions and finaly build will start

Integrating APNs and FCM for iOS

We need to integrate APNs with FCM to enable push notifications on iOS devices.

  1. Go to yout apple developer account — https://developer.apple.com
  2. Then Certificates, Identifiers & Profiles — https://developer.apple.com/account/resources/certificates/list
  3. Click keys and add new key
  4. Add new key and from the list check push Apple Push Notifications service
  5. Continue and register
  6. Finaly copy key ID and Download configuration
Keys page and adding new key

6. You can now include the file and Key ID in your Firebase Project. To do this, go to the Project settings on the Firebase Console and open the Cloud Messaging tab.

7. Find your iOS app configuration under the iOS app configuration section.

8. Upload the downloaded file and provide the Key ID and Team ID in the appropriate fields.

Firebase apple config

When building your app for production, you’ll need to create a new App Identifier that is linked to the application you’re developing in order for messaging to work properly. Follow these steps:

  1. On the Certificates, Identifiers & Profiles page, click on Identifiers and register a new App Identifier.
  2. Select App IDs and click Continue.
  3. Scroll down to the Push Notifications capability (as well as any others your app requires) and enable it, then click Continue.

Send notifications

In order to send notifications to a device, you will need to obtain its device token. This can be done using the following code snippet:

const requestUserPermission = async () => {
const authStatus = await messaging().requestPermission();
const enabled =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;

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

useEffect(() => {

if (requestUserPermission()) {
messaging()
.getToken()
.then(
token => console.log(token)
);
}
}, []);
  1. Go to the Firebase console and select Cloud Messaging.
  2. Create a new campaign by clicking the New campaign button.
  3. Compose your notification, including the title, body, and any other relevant information.
  4. Add the device token(s) of the intended recipient(s) to the appropriate field(s).
  5. Preview the notification to ensure it looks as expected.
  6. Send the notification.
Compose notificaiton and add device token

If you want to send notification from server, here is nodejs example

var admin = require("firebase-admin");

var serviceAccount = require("<FIREBASE_ADMIN_SDK_JSON>"); //https://firebase.google.com/docs/admin/setup

admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});

const messaging = admin.messaging();

// This registration token comes from the client FCM SDKs.
const registrationToken =
"<DEVICE_TOKEN>";

const message = {
notification: {
title: "Test title",
body: "Test description",
},
token: registrationToken,
};

// Send a message to the device corresponding to the provided
// registration token.
admin
.messaging()
.send(message)
.then((response) => {
// Response is a message ID string.
console.log("Successfully sent message:", response);
})
.catch((error) => {
console.log("Error sending message:", error);
});

--

--

Andria shonia

Proficient software engineer with 5+ years crafting intuitive digital experiences. A UX/UI expert passionate about delivering high-quality solutions.