Send Push notification with Firebase and React

Introduction

What are push notifications and how to use them? Well, push notifications are messages or alerts which are sent (pushed) to the user's device, no matter if the user's application is opened or not. When we talk about web push notifications, the application will receive a message pushed to it no matter if it is open or not, so the user can fetch all "unread" messages when it comes online or open the app. Firebase Cloud Messaging, or FCM as it will be called further in the post, is a cross-platform messaging solution from Google which allow us to reliably send messages to our clients.

As we can see in the official FCM documentation :

Firebase Cloud Messaging (FCM) is a cross-platform messaging solution that lets you reliably send messages at no cost. Using FCM, you can notify a client app that new email or other data is available to sync. You can send notification messages to drive user re-engagement and retention. For use cases such as instant messaging, a message can transfer a payload of up to 4000 bytes to a client app.

Prerequisite

The main idea of this post is to show you how you can implement Firebase with React, for this you need to have a Google developer account. We will use create-react-app in order to create our test app and we will use react-toastify for showing the toast message of our push notifications.

Creating our app

The first thing we need to create our project is to run the following commands to get started.

npx create-react-app firebase-test-app
npm install firebase react-toastify
npm start

First, we used create-react-app to init our project which will be used for this demo, next thing is installing firebase and react-toastify npm modules and afterward start the project.

Firebase setup

You need to create an account if you don't already have one, so go to firebase and register, if you already have an account just skip this step and go to the Firebase console. After we register we need to create a new project and when we have a project created we need to register the application afterward which we will get firebase configuration. In further sections, we will see this step by step.

Creating project

This step is pretty easy, you need to go to Create new project and you will get input for name of the project, so go ahead and enter name for the project. If you have no projects you will see Create new project button but if you already have a project, you will have tiles with existing projects and go to + Add project. After you enter name you will be prompted to check or uncheck whether you want to use Google Analytics. You can feel free to uncheck it because we don't need it know but if you leave it checked it will make no harm. After all, data are filled, you will see the loader and afterward will be redirected to the project screen.

Application registration

When your project is created you will need to choose the platform you want to connect to your application, so we are using web project so we can select web option. You will be prompted again to enter a name for the application you are about to register, after entering the name you can proceed and configuration for your application will be generated, which will be needed to get a token, connect and use the FCM. This given configuration you can copy and we will provide it to our React application. When this last step is finished we have our FCM project set up and we can start building our application. For the whole setup flow you can reference to image below, where I will show you how it looks.

Representation of Firebase new project and app creation

React application setup

We have our Firebase project generated as well as configuration, so now we want to connect FCM to our application.

1.) Create firebaseNotification.js inside of src and add the following code:

import { initializeApp } from 'firebase/app';

// Insert firebase config that you got from Firebase console after creating app
var firebaseConfig = {
    apiKey: "xxxxxxxxQIi6qkwdBTHnqSF0758hIlfUycHiGk",
    authDomain: "xxx-xxxxxx-5392c.firebaseapp.com",
    projectId: 'xxxxx,
    ...
};
initializeApp(firebaseConfig);

2.) Import the Firebase messaging module from the Firebase npm module into firebaseNotification.js:

import { getMessaging } from "firebase/messaging";
...
const messaging = getMessaging();

3.) One of the important things is to fetch tokens, so we need to create a function like requestToken which will use firebase's getToken method. It will subscribe our application to push notifications (FCM) and it will require permission if it's not granted so be sure to Allow notifications in the browser to be able to receive a token. If our application has the necessary permission we will get a token which we will later use to send push notifications.

import { getMessaging, getToken} from 'firebase/messaging';
...
export const requestToken = () => {
  return getToken(messaging, { vapidKey: "<YOUR_VAPID_KEY>" })
    .then((currentToken) => {
      if (currentToken) {
        console.log('current token for client: ', currentToken);
        // Do something with the token
      } else {
        // Token failed to fetch
        console.log('No registration token available. Request permission to generate one.');
      }
    })
    .catch((err) => {
      console.log('An error occurred while retrieving token. ', err);
    });
};

4.) Next thing we are required to do before sending push notifications, we need to generate VAPID or Voluntary Application Server Identification in order to use the getToken method. In order to do this, go to Project Settings on your Firebase console, navigate to the Cloud Messaging tab and go to the Web configuration section. You will find the Web Push certification tab here you can click on Generate key pair.

Generate VAPID key pair

Create Notification component

Now that we have the VAPID key, we can import our firebaseNotification.js to our application, so the next thing we will due we will create the Notification.jsx react component. Here we will use react-toastify which I prefer but you can use react-hot-toast or some other as you wish, to get a toast message on FCM notification and we will import our requestToken and onMessageListener in order to fetch token and afterward listen to new messages as they arrive. Pay attention to the import request token method and call it in order for FCM to work as you can see in the code below. We will create notify method inside of useEffect in order to notify and push notifications to clients when a message arrives. You can use the following code:

import React, {useState, useEffect } from "react";
import { requestForToken, onMessageListener } from "./push-notification";

import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

const Notification = () => {
    const [notification, setNotification] = useState({title: '', body: ''});
    requestForToken();
    const notify = () => toast(<ToastDisplay/>);

    function ToastDisplay() {
        return (
          <div>
            <h2 style={{fontSize:'1em'}}><b>{notification?.title}</b></h2>
            <p style={{fontSize:'0.7em'}}>{notification?.body}</p>
          </div>
        );
    };


    useEffect(() => {
        if (notification?.title ){
         notify(notification)
        }
      }, [notification])

    onMessageListener()
      .then((payload) => {
        setNotification({title: payload?.notification?.title, body: payload?.notification?.body});     
      })
      .catch((err) => console.log('failed: ', err));

    return (
      <ToastContainer theme="dark"/>

    )
}

export default Notification

Receiving messages

So we can separate two ways of receiving messages foreground and background. The first means that this listener will listen to the messages when the application is focused and opened and the other one means that the service worker will listen to the notification messages when our application is closed.

Foreground listener

.Foreground listener we already added and it will be used when our application is open and focused. This is code we already added to the firebaseNotification.js:

import { getMessaging, getToken, onMessage } from 'firebase/messaging';
const messaging = getMessaging();

export const onMessageListener = () =>
  new Promise((resolve) => {
    onMessage(messaging, (payload) => {
        console.log("ON MESSAGE !!!!")
      console.log("payload", payload)
      resolve(payload);
    });
  });

Background listener

Service worker or as we will call it background listener we will explain due to it being a less known concept. FCM requires one specific file which will be called firebase-messaging-sw.js inside of the public folder, so first go ahead and create this file. Let's explain this part, in order to handle background messages, we will need to use a service worker which is a script that the browser can run in the background completely separated from the web page and it can enable features that not requires a web page or user interaction.

After creating you can add the following code:

// Scripts for firebase and firebase messaging
importScripts('https://www.gstatic.com/firebasejs/8.2.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.2.0/firebase-messaging.js');

// Initialize the Firebase app in the service worker by passing the generated config
const firebaseConfig = {
  apiKey: `<YOUR_FIREBASE_MESSAGING_API_KEY>`,
  authDomain: `<YOUR_FIREBASE_MESSAGING_AUTH_DOMAIN>`,
  projectId: `<YOUR_FIREBASE_MESSAGING_PROJECT_ID>`,
  storageBucket: `<YOUR_FIREBASE_MESSAGING_STORAGE_BUCKET>`,
  messagingSenderId: `<YOUR_FIREBASE_MESSAGING_SENDER_ID>`,
  appId: `<YOUR_FIREBASE_MESSAGING_APP_ID>`,
  measurementId: `<YOUR_FIREBASE_MESSAGING_MEASUREMENT_ID>`,
};

// Initialization of the application
firebase.initializeApp(firebaseConfig);

// Retrieve firebase messaging
const messaging = firebase.messaging();

// Background message listener
messaging.onBackgroundMessage(function(payload) {
  console.log('Received background message ', payload);
 // Customize notification here
  const notificationTitle = payload.notification.title;
  const notificationOptions = {
    body: payload.notification.body,
  };

  self.registration.showNotification(notificationTitle,
    notificationOptions);
});

Sending push notifications

Let's test and check out whether it is working as expected, in order to test it go to the Firebase console and send a message

  • In your project menu, go to Engage menu and there you will find the submenu Cloud messaging

  • Click on the button Create your first campaign

  • You will be prompted in the dialog to choose message type and platform, go on and select Firebase Notification messages and click on Create

  • You will get input fields, enter the Notification title and Notification text as these two fields are necessary, others we will skip them.

    Compose notification

  • On the right side, you have to Send test message button and the modal will pop up. Here you can enter the token you recieved and after that click on Test

  • And there. You have your notification toast inside your application. If you followed this guide you should get the same as on the image below. NOTE that we have received notification for our application and toast is visible. Unfortunately, it is not visible on the video but I received as well Windows notification for the other application I selected when I was choosing tokens (you can see that I selected two tokens) and that application is closed and not running but it service worker is working and it detected notification so here you can see the usage of service work as well.

    Recieving notification

Troubleshooting

At the first run, it is possible that you get permissions not being granted but blocked instead which means that you need to Allow notifications in your browser. If you get this error, be sure to check notification permission in the browser.

Notification permission in browser

Another error I encountered was missing the required authentication credential which meant that I passed the wrong VAPID_KEY. For more information about this and other possible errors on which you can encounter, please check this post