Push notifications on your phone can be super useful. Are you thinking about implementing them in your application but aren't sure where to start? Maybe the process seems too complicated and that's the reason your users still aren't receiving push notifications. If that's the case or if you just want to know how to tackle push notifications in React Native, then this article is for you.
I would like to walk you step-by-step through how to build a Push Notification System based on Firebase Cloud Message and Notifee.
In the process, you will create a new React Native project to test on an Android device, a simplified version of the backend that supports sending notifications to the device. Additionally, you'll test two types of notifications so that you can see different approaches yourself.
The goal is to leave you with the feeling that the implementation of push notification systems is not that difficult and that you can use the acquired knowledge in your project.
So let's get started!
If you'd like to read more about React Native, check out our blog post about the applications that were developed using this technology:
25 Innovative React Native Apps Examples in 2023
Step 1. Create a new React Native project
Below you will find detailed documentation and a description of how to create a React Native project, and information about all the dependencies needed to run this project:
Assuming you have all the dependencies listed above installed, use the command below to initialize the project:
> npx react-native init notifications-test
Additionally, install the necessary libraries:
> npm i react-native-firebase/app react-native-firebase/messaging
> npm install --save @notifee/react-native
After creating the project, open it in your favorite editor (VS Code, Sublime) and replace the contents of the App.js
file with the following piece of code:
import React from 'react';
import {Button, SafeAreaView, StyleSheet, Text, View} from 'react-native';
const App = () => {
const handleSendNotification = () => {
return;
};
const handleSetTheAlarm = () => {
return;
};
return (
<SafeAreaView style={styles.wrapper}>
<Text>PUSH NOTIFICATIONS TEST</Text>
<View style={styles.buttonsWrapper}>
<View style={styles.buttonWrapper}>
<Button title="SEND NOTIFICATION" onPress={handleSendNotification} />
</View>
<View style={styles.buttonWrapper}>
<Button
title="SET ALARM"
color="red"
onPress={handleSetTheAlarm}
/>
</View>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
wrapper: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
backgroundColor: 'white',
},
buttonsWrapper: {
marginTop: 50,
},
buttonWrapper: {
marginBottom: 20,
},
});
export default App;
Then run the application. The result should match the screen below:
Step 2. Create a Firebase project
Create a project in Firebase Console
. The link below provides step-by-step instructions on how to do this.
https://console.firebase.google.com/u/0/
The next step is to add Firebase to the React Native project.
The application will be tested on an Android device, so after selecting this type of application, go through all the steps - some of them will require changes to the application code.
Step 3. Creation of the server part
A new folder should be created in the selected place on the disk, e.g.,"notifications-test-api". Add the index.js
file to it, where the code will be placed. The following commands will initiate a new node.js project and install all the necessary dependencies.
> npm init
> npm install fastify firebase-admin nodemon
For the backend to work with Firebase, you need to download serviceAccountKey
, which must be added to the project folder.
In order to download this file, open the Firebase project → enter theproject settings, then in the Service accounts tab and select the Generate new private key
button.
The next step is to paste the code below into the index.js
file and replace the path leading to the previously downloaded file.
const admin = require("firebase-admin");
const fastify = require("fastify")({ logger: true, keepAliveTimeout: 5000 });
const util = require("util");
// Zastąp "path/to/serviceAccountKey.json"
const serviceAccount = require("path/to/serviceAccountKey.json");
const delay = util.promisify(setTimeout);
// Initialize Firebase
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
// Remote Push Notification
async function sendAlarmNotification(token) {
return admin.messaging().send({
token,
notification: {
body: "Hi here's your alarm",
title: "It's time to wake up",
},
data: {
type: "alarmNotification",
},
});
}
// Partial Push Notification
async function sendPartialNotification(token) {
return admin.messaging().send({
token,
data: {
type: "partial_notification",
notifee: JSON.stringify({
body: "I'm your push notification",
android: {
channelId: "default",
},
}),
},
});
}
// Declare a notification route
fastify.post("/notifications", async (request) => {
await delay(5000);
await sendPartialNotification(JSON.parse(request.body).token);
return "OK";
});
// Declare a alarm route
fastify.post("/alarm", async (request) => {
await delay(5000);
await sendAlarmNotification(JSON.parse(request.body).token);
return "OK";
});
// Run the server
const start = async () => {
try {
await fastify.listen(3000);
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
};
start();
Now we have a working server! It can be run with the command:
The function below is responsible for sending messages with the notification
and data
keys. Messages sent in this format will cause Push Notifications to appear on the device and be displayed by the operating system. Our application has no influence on the display of this type of notification.
// Remote Push Notification
async function sendAlarmNotification(token) {
return admin.messaging().send({
token,
notification: {
body: "Hi here's your alarm",
title: "It's time to wake up",
},
data: {
type: "alarmNotification",
},
});
}
In the latter case, the message will only be sent with the data
key. A such notification will not be displayed automatically. In order to display a such notification, we need to implement the logic that will handle it.
For the purposes of this article, the setTimeout
function has been used, which will delay sending messages so that you have time to close the application after pressing the button sending the query to the server. The notification will appear on the device only when the application is minimized. You can read more about it here: https://rnfirebase.io/messaging/usage#notifications.
Step 4. Support and display of Remote Push Notifications on the device
The last step is to implement the push notification support in our React Native application, i.e .:
- device registration in Firebase Cloud Messages,
- downloading a registered token,
- as a simplification, we will send the token in every query to the server, it is required for the server to identify the recipients of Push Notification (in a real project, I suggest that it be kept, e.g. in the database).
These assumptions are fulfilled by the code below, which should be added to App.js
.
const [token, setToken] = useState('');
const getFirebaseToken = async () => {
// Register the device with FCM
await messaging().registerDeviceForRemoteMessages();
// Get the token
const generatedToken = await messaging().getToken();
return generatedToken;
};
useEffect(() => {
async function fetchData() {
const generatedToken = await getFirebaseToken();
setToken(generatedToken);
}
fetchData();
}, []);
In order to react by the application to an incoming notification, React Native Firebase
provides two methods: onMessage
and setBackgroundMessageHandler
. In this project, we will use the setBackgroundMessageHandler
function, which handles messages from FCM while the application is running in the background (this is a prerequisite for seeing device notifications).
useEffect(() => {
messaging().setBackgroundMessageHandler(onMessageReceived);
}, []);setBackgroundMessageHandler takes a function that is called in response to this event.
const onMessageReceived = async message => {
// do sth
};
At this stage, we can send the first notification. Let's start with the one defined on the server side with the notification and data keys. To see it, add the function that will be used later when pressing the 'SET ALARM' button.
const handleSetTheAlarm = () => {
fetch(`${BASE_URL}/alarm`, {
method: 'POST',
body: JSON.stringify({
token,
}),
});
};
After pressing the button, the server will receive the query and send the FCM notification after a delay (approx. 10 seconds). Remember that it will only appear when you exit the application (the application will run in the background). After a while, you should see your first notification that was displayed by the operating system.
Remember that now you can write support for such a notification in the onMessageReceived
(notification) function. For example, you can try to add navigation to your project and after clicking on such notification, move to another place in the application.
Notifee
Now, I will show you how we can use the installed Notifee
library to handle the second type of notification, i.e. the one that was defined on the server side with only thedata
key.
This time you need to implement support for such a notification in the onMessageReceived (notification) function.
The message defined on the server has the type field:
data: {
type: "partial_notification",
...
},
It can be used to recognize what notification has been sent to the device and handle it accordingly.
const onMessageReceived = async message => {
if (!message || !message.data || !message.data.type) {
return;
}
if (message.data.type === 'partial_notification') {
// add code to display notification
}
};
When testing a notification that has only been defined with the data
key, it will not be displayed on the device like the previous time. For this, we will use the Notifee library to display it.
The notification will be displayed using the displayNotification
method from theNotifee
library.
displayNotification(notification: Notification): Promise<string>;
This function takes an object of the Notification
type as a parameter, in our case we want to display a notification consisting of a title and content. Additionally, when displaying notifications on an Android device, it is required to pass an additional parameter channelId
.
For more information, see: https://notifee.app/react-native/docs/android/channels.
const onMessageReceived = async message => {
if (!message || !message.data || !message.data.type) {
return;
}
if (message.data.type === 'partial_notification') {
// Transormowanie danych do obiektu
const notifeeData = JSON.parse(message.data.notifee);
await notifee.displayNotification({
title:
'<p style="color: #4caf50;"><b>Hello! I was defined on frontend</span></p></b></p> 🙀',
body: notifeeData.body,
android: {
channelId,
},
});
}
};
Next, define the function that will be called after pressing the 'SEND NOTIFICATION' button.
const handleSendNotification = () => {
fetch(`${BASE_URL}/notifications`, {
method: 'POST',
body: JSON.stringify({
token,
}),
});
};
Ready! Just like the last time, after pressing the button and minimizing the application, the result will be the appearance of the following notification on the device.
Summary
I hope you will be able to set up your first push notification with this guide. If you have any questions, feel free to contact me on LinkedIn at