Push Notifications in React & Firebase

Push Notifications in React & Firebase

Table of contents

Getting users to engage and re-engage in mobile or web apps is a challenge for every business. One effective solution to reach users who are online is Push Notifications. Push notifications are effective and fast solutions for getting users to know what is happening in your app or we should say in the business.

So in this tutorial, we are going to implement Push Notifications in React.js, and for the backend firebase cloud messaging (FCM) will be used, but you can use your system. We will implement it using create-react-app which is an official way to create single-page React applications. It offers a modern build setup with no configuration. Read more here about CRA.

So let’s dive into it. But if you want to access the code, here’s the repo for the demo app.

If you want to use push notifications without Firebase, simply use the Notifications API object provided by the browser, as we are using in this tutorial on notification on button click. The Notification interface of the Notifications API is used to configure and display desktop notifications to the user. These notifications' appearance and specific functionality vary across platforms but generally, they provide a way to asynchronously provide information to the user.

But for this tutorial, we are using Firebase to allow permission and use notifications. So now head towards practical implementation.

First, we have to create a Firebase project in your Google Firebase Console

Create a react application by typing in the command prompt :

npx create-react-app react-notify

Now, after creating your react project, you have to install Firebase dependencies by:

yarn add firebase

Now this command will add Firebase to your project. You will see this package is included in the package.json file.

{
  "name": "react-notify",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.3.2",
    "@testing-library/user-event": "^7.1.2",
    "firebase": "^7.6.2",
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "react-scripts": "3.3.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

Now after installing Firebase dependency, we have to configure our Firebase project into our app. First, we will create a file under the src folder name push.js. Although you could create with any name you want. Here’s our push.js file:

import firebase from "firebase";
export const initializeFirebaseWithFCMToken = () => {
  firebase.initializeApp({
    apiKey: "your-API-key",
    authDomain: "react-notify-****.firebaseapp.com",
    databaseURL: "https://react-notify-****.firebaseio.com",
    projectId: "react-notify-****",
    storageBucket: "react-notify-****.appspot.com",
    messagingSenderId: "**********",
    appId: "********************",
    measurementId: "*********"
  });

You will get these values from the project settings tab and after scrolling down, check the Firebase SDK snippet and copy-paste configuration. In the above code, we have imported our firebase module and created a function named initializeFirebaseWithFCMToken() which is responsible for initializing our app but we have to include this file into our index.js file, where our main app component is rendered so that we can access this in our whole project.

Now our index.js file will look like this:

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import firebase from "firebase";
import { initializeFirebaseWithFCMToken } from "./push";

ReactDOM.render(<App />, document.getElementById("root"));

initializeFirebaseWithFCMToken();  // firebase configuration here

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.register();

And make sure, to convert serviceWorker.unregister(); to serviceWorker.register();

Service Worker

A service worker is a script that your browser runs in the background, separate from a web page, opening the door to features that don't need a web page or user interaction. Today, they already include features like push notifications and background sync. You have to register it to work notifications properly. Read more about service workers.

Setting up service workers to receive messages

After registering it, we will then create a firebase-messaging-sw.js in the public folder of the project. This is a future service worker who will be receiving the messages in the background mode.

self.addEventListener('push', event => {
  const data = event.data.json();
  console.log('New Notification', data);
  const options = {
    body: data.body
  }
  event.waitUntil(
    self.registration.showNotification(data.title, options)
  )
});

Here is the firebase-messaging-sw.js file, where we are listening to push events to get our notification. So, the event.waitUntil method is used to tell the browser not to terminate the service worker until the promise passes to wait until is either resolved or rejected. In this case, we will receive notifications asynchronously. (see the specification of the waitUntil method).

That’s it for the initial setup, now we will do something when our app is loaded for the first time, it will show the popup for Show Notification like below screenshot below.

When we click the allow button for permission, it will return the Firebase cloud messaging (FCM) token and we can do whatever we want either to store it in the database or local storage for access to the whole app. But to show the initial popup for permission, we have to add some code to push.js file, which is included in our index.js so that when our app loads it will show an ask for notification permission.

import firebase from "firebase";
export const initializeFirebaseWithFCMToken = () => {
  firebase.initializeApp({
    apiKey: "your-api-key",
    authDomain: "react-notify-***.firebaseapp.com",
    databaseURL: "https://react-notify-****.firebaseio.com",
    projectId: "react-notify-***",
    storageBucket: "react-notify-***.appspot.com",
    messagingSenderId: "**********",
    appId: "********************",
    measurementId: "*********"
  });

  // ********** code for adding notification functionality*******

  // retrieving firebase messaging object
  const messaging = firebase.messaging();
  messaging
    .requestPermission()
    .then(() => {
      console.log("Have Permission");
      return messaging.getToken();
    })
    .then(token => {
      console.log(token);
    })
    .catch(err => console.error(err));

  // Add the public key generated from the console, here.
  messaging.usePublicVapidKey("****************");

  // onMessage event for getting payload from notification
  messaging.onMessage(function(payload) {
    console.log("onMessage", payload);
  });
};

Now this firebase.messaging() object will request permission by requestPermission method which is an asynchronous method returning promise. If we click allow, then we will see the Have Permission message on the console and it’ll return a token. If everything works perfectly then, our callback will print on the console our messaging token as well, which we can save anywhere we want. For development purposes, check Update on reload so it can reflect changes every time you change some code and reload it.

Note: Make sure to change the permission setting from the URL bar, as shown above, if you have used http://localhost:3000 before, otherwise it will not work properly.

You may have noticed, that the messaging.onMessage() event. It is responsible for getting notifications on backgrounds that is when your browser is closed. You will see the notification with the payload object even if the browser is closed, so you know who sent you a message or who likes your profile picture.

Now you probably notice the button in our react component as well. I’ve created this button for getting notified if it’s permitted.

Our cursor is not showing in this screenshot, but when I click it, it notifies me that you have already been granted. If you don't allow it or block it, this notification will not pop up and you will get an error for declined.

Now here’s the code for our button and the logic of notification operation.

import React from "react";
import logo from "./logo.svg";
import "./App.css";

const getNotification = () => {
  if (!"Notification" in window) {
    alert("This browser does not support desktop notification");
  }
  // Let's check whether notification permissions have already been granted
  else if (Notification.permission === "granted") {
    // If it's okay let's create a notification
    var notification = new Notification("Already been granted!");
    console.log(notification);
  }

  // Otherwise, we need to ask the user for permission
  else if (Notification.permission !== "denied") {
    Notification.requestPermission().then(function(permission) {
      // If the user accepts, let's create a notification
      if (permission === "granted") {
        var notification = new Notification("First Time granted");
        console.log(notification);
      }
    });
  }
};

function App() {

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
        <br />
        <button onClick={getNotification}>Click for Permission</button>
      </header>
    </div>
  );
}

export default App;

Here’s our functional component, where we created a function named getNotification and in this function, we are verifying if the Notification object is available on the window. If it’s available then we implement the logic for handling permission status and show notification according to our status.

That’s pretty much it for this article, you can access the code here. Thank you if you made it here.

Here are some other resources, from where you can get deep dive into push notifications:

  1. https://firebase.google.com/docs/cloud-messaging/js/client

  2. https://codeburst.io/how-to-add-push-notifications-on-firebase-cloud-messaging-to-react-web-app-de7c6f04c920

  3. https://www.freecodecamp.org/news/how-to-add-push-notifications-to-a-web-app-with-firebase-528a702e13e1/