How To Add Firebase Push Notifications In Next JS (React)

Sarafathulla S
5 min readApr 16, 2020

--

We will setup push notifications on NextJS by using Firebase Cloud Messaging which will receive notifications even in the background or when the app is closed.

Setting up FCM

First you have to create a project in firebase (https://console.firebase.google.com) to integrate FCM. From the project settings on console, you can add Firebase App to your web application.

You will find the following config in Firebase console > Project Settings > General > Firebase SDK snippet > config

var config = {
apiKey: ***************,
authDomain: ************,
databaseURL: ***********,
projectId: **********,
storageBucket: **********,
messagingSenderId: **********
};

From the above code snippet, you just need the messagingSenderId. So you can remove the rest.

Setting up the Firebase SDK

Now that you have firebaseConfig all set up, it’s time to use that in your application. Install firebase plugin in your NextJS app using npm or yarn.

yarn add firebase
# or
npm install firebase

Connecting with Firebase Cloud Messaging

Create a webPush.js file inside your project directory where you will initialize and get the FCM token. For storing FCM token we are using localforage.

yarn add localforage
# or
npm install localforage

localForage improves the offline experience of your web app by using asynchronous storage (IndexedDB or WebSQL) with a simple, localStorage-like API.

Add the below lines of code in webPush.js

import ‘firebase/messaging’;
import firebase from ‘firebase/app’;
import localforage from ‘localforage’;
const firebaseCloudMessaging = {//checking whether token is available in indexed DBtokenInlocalforage: async () => {
return localforage.getItem(‘fcm_token’);
},
//initializing firebase appinit: async function () {if (!firebase.apps.length) {firebase.initializeApp({
messagingSenderId: **********
});
try {const messaging = firebase.messaging();
const tokenInLocalForage = await this.tokenInlocalforage();
//if FCM token is already there just return the tokenif (tokenInLocalForage !== null) {
return tokenInLocalForage;
}
//requesting notification permission from browserconst status = await Notification.requestPermission();if (status && status === ‘granted’) {//getting token from FCMconst fcm_token = await messaging.getToken();if (fcm_token) {//setting FCM token in indexed db using localforagelocalforage.setItem(‘fcm_token’, token);console.log(‘fcm token’, token);//return the FCM token after saving itreturn token;
}
}
} catch (error) {
console.error(error);
return null;
}
}
},
};
export { firebaseCloudMessaging };

Adding Service Workers

Service workers acts like a middleware between your browser and external content (like images, fonts, APIs, etc.) by intercepting HTTP requests made by your browser. This can lead to a number of different strategies for returning assets to users such as initially showing low-res images before swapping in the correct version or implementing a stale-while-revalidate pattern for regularly updated content.

In case of push notifications, Push messages are delivered to a Service Worker that runs in the origin of the web application, which can use the information in the message to update local state or display a notification to the user.

Scope of a Service Worker

Service Worker is scoped to ./ from the path on which it is loaded, by default. For example, in our Next JS application a service worker loaded from app.com/.next/service-worker.js would be scoped to the /.next path.

This is ok for a lot of use cases. However, the service worker to be registered would be loaded from the root scope (https://my.app.com/.next/service-worker.js) which, in Next JS apps, requires a custom server implementation.

Creating a Custom Service Worker

The purpose of creating this service worker is to cache the FCM initialization.

Create the following code in the file
public/firebase-messaging-sw.js (or) static/firebase-messaging-sw.js

importScripts(‘https://www.gstatic.com/firebasejs/7.9.1/firebase-app.js');importScripts(‘https://www.gstatic.com/firebasejs/7.9.1/firebase-messaging.js');if (!firebase.apps.length) {firebase.initializeApp({
messagingSenderId: **********
});
firebase.messaging();//background notifications will be received herefirebase.messaging().setBackgroundMessageHandler((payload) => console.log(‘payload’, payload));
}

importScripts() tells your service worker script to pause its current execution, download additional code from a given URL, and run it to completion in the current global scope. Once that’s done, the main service worker script resumes execution.

Setting Up a Custom Server

Babel takes care of the Automation of server restart, Code Transpiling, Hot Reloading etc.. But unfortunately the default setup doesn’t allow us an easy way to scope the service worker to our root path so a custom server is needed.

The following server.js is enough to make things work as expected:

const express = require(‘express’);
const next = require(‘next’);
const port = parseInt(process.env.PORT, 10) || 3000;
const dev = process.env.NODE_ENV !== ‘production’;
const app = next({ dev });
const handle = app.getRequestHandler();app.prepare().then(() => {
const server = express();
server.get(‘/service-worker.js’, (req, res) => {
app.serveStatic(req, res, ‘./.next/service-worker.js’);
});
//scoping the service workersconst serviceWorkers = [
{
filename: ‘service-worker.js’,
path: ‘./.next/service-worker.js’,
},
{
filename: ‘firebase-messaging-sw.js’,
path: ‘./public/firebase-messaging-sw.js’,
},
];serviceWorkers.forEach(({ filename, path }) => {
server.get(`/${filename}`, (req, res) => {
app.serveStatic(req, res, path);
});
});
server.get(‘*’, (req, res) => {
return handle(req, res);
});
server.listen(port, (err) => {
if (err) throw err;
});
});

Initializing And Receiving Foreground Push Messages

After creating and setting up the service worker we need to initialize firebase config to receive foreground messages which we have already created in webPush.js.

import React, { useEffect } from ‘react’;import { firebaseCloudMessaging } from ‘./webPush’;function App() {useEffect(() => {
setToken();
async function setToken() {
try {
const token = await firebaseCloudMessaging.init();
if (token) {
getMessage();
}
} catch (error) {
console.log(error);
}
}
function getMessage() {
const messaging = firebase.messaging();
messaging.onMessage((message) => console.log(‘foreground ’, message));
}
}, []);return <div></div>;
}
export default App;

Test FCM Notification with POSTMAN

Firebase allows us to pass messages from our app server to client apps via FCM.

https://fcm.googleapis.com/fcm/send

All we need is an FCM server token and FCM token which we will getting from the webPush.js file that we created before.

Go to Firebase console > Project Settings > Cloud Messaging and copy server token.

Open Postman > import > Paste Raw Text and paste the following curl and new request will be created

Replace {{FCM server token}} with the server token which we copied from the firebase console and {{FCM token}} with the token received from the webPush.js file

curl --location --request POST 'https://fcm.googleapis.com/fcm/send' \
--header 'Content-Type: application/json' \
--header 'Authorization: key={{FCM server token}}' \
--header 'Content-Type: text/plain' \
--data-raw '
{
"to" : "{{FCM token}}",
"data": {
"notification": {
"title": "Hello",
"body": "world"
}
}'
Headers with FCM server token
Post body

Once you finish setting up the FCM server token in header and FCM token in post body, press Send to send the notification.

If the app is open and active in the browser window you will be receiving the notification in the console tab of developer options

messaging.onMessage((message) => console.log(‘foreground ’, message));

If the app is in the background you will receive a popup out of nowhere, which will be there for sometime and then fades away. This can be triggered in firebase-messaging-sw.js service worker file.

firebase.messaging().setBackgroundMessageHandler((payload) => console.log(‘payload’, payload));

Keep Clapping Hoomans

--

--