Introduction
Firebase was a company that Google acquired in 2014. Since then, Google has made several enhancements to the platform to the point that it now pitches Firebase as its one-stop backend-as-a-service solution for mobile apps. Among several Google solutions like centralized authentication, realtime databases, cloud functions (which is Google’s equivalent of AWS lambda) that are a part of the Firebase umbrella, there is one that is picked as a go-to solution for managing app notifications. That is Firebase Cloud Messaging (FCM), formerly Google Cloud Messaging (GCM). In today’s article, we will look into the process to enable the push-notifications feature in our front-end React application.
The React app
Basic app
For the purpose of this article, we will be using a skeleton React app created by the create-react-app
npm package. So, let’s create a folder named firebase-notifications
and inside of its, lets run the command (make sure you have npm and npx installed):
npx create-react-app fire_client
This creates a basic React app. To run the app, lets cd
into the folder and run npm run start
so that the app starts on localhost and we get this familiar page:
Library integration
Now, let’s add the dependencies for integrating the client-side functionality of Firebase Cloud Messaging and react-bootstrap
for getting a few Bootstrap-based React UI components:
npm install --save firebase react-bootstrap bootstrap
Bootstrap setup
Once that is done, let’s change the src/App.js
file like so:
import logo from './logo.svg'; import './App.css'; import {useState} from 'react'; import { getToken, onMessageListener } from './firebase'; import {Button, Row, Col, Toast} from 'react-bootstrap'; import 'bootstrap/dist/css/bootstrap.min.css'; function App() { getToken(); const [show, setShow] = useState(false); onMessageListener().then(message => { setShow(true); }).catch(err => console.log('failed: ', err)); return ( <div className="App"> <Toast onClose={() => setShow(false)} show={show} delay={3000} autohide animation style={{ position: 'absolute', top: 20, right: 20, }}> <Toast.Header> <img src="holder.js/20x20?text=%20" className="rounded mr-2" alt="" /> <strong className="mr-auto">Notification</strong> <small>12 mins ago</small> </Toast.Header> <Toast.Body>There are some new updates that you might love!</Toast.Body> </Toast> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <Button onClick={() => setShow(true)}>Show Toast</Button> </header> </div> ); } export default App;
We’ve imported the bootstrap CSS in App.js
and also, there’s a show toast button that brings up a toast notification which auto-hides after three seconds. Here’s how that looks:
With that in place, we can now move to the Firebase setup.
Firebase setup
Project creation
Login/sign up into the Firebase console and click on Add project. Follow the steps to add the project:
- Give the project an appropriate name
- Enable/disable analytics based on your preference
- Wait for the setup to complete
Once done, redirect to the project home screen which should look something like this:
Project link
Now we need to create a link between the Firebase project and the frontend React app that we just now created. We would be doing that using the Firebase project config. Out of the IOS, Android, and web options that are visible in the previous screenshot, click on the web (</>) icon. This takes us to the Register app UI:
Assign a nickname to the React app that we created in the earlier step and click on the Register app button. Wait for a while as the screen generates a config which we will be soon integrating in the React app. This is what the final generated config should look like:
The firebaseConfig
object is the one that we will be integrating into our React app which will link it to this particular firebase project.
Linking projects
Let’s create a new file called firebase.js
in our React codebase and add these lines of code there:
import firebase from 'firebase/app'; var firebaseConfig = { apiKey: "YOUR_API_KEY", authDomain: "YOUR_AUTH_DOMAIN", projectId: "YOUR_PROJECT_ID", storageBucket: "YOUR_STORAGE_BUCKET", messagingSenderId: "YOUR_MESSAGING_SENDER_ID", appId: "YOUR_APP_ID" }; firebase.initializeApp(firebaseConfig);
Make sure to replace the firebaseConfig
with the one that got generated in the previous step. Importing the firebase.js
file in App.js
now links both the projects together. Let’s now move to setting up cloud messaging on Firebase.
Integrate cloud messaging
As a next step, we need to generate a web push certificate key. Navigate to the cloud messaging tab for your project and scroll to the Web configuration section. Under Web push certificates, click on Generate key pair. Note down the key that gets generated.
Back in the firebase.js
file, we now need to enable messaging. Add this line to the imports:
import 'firebase/messaging';
And then, we can access the messaging object from the firebase
object like so:
const messaging = firebase.messaging();
Notification permissions and registering client
In order to send push notifications to the browser, we first need to get permission from the user. When we do that, it opens the Enable Notifications on this site? popup that you might have seen on some sites. The way to initiate that request is by calling the getToken
method that firebase provides.
Before that, let’s add a variable to the state of the App.js
file which will keep track of whether we have access to the notifications or not:
const [isTokenFound, setTokenFound] = useState(false); getToken(setTokenFound); // inside the jsx being returned: {isTokenFound && <h1> Notification permission enabled 👍🏻 </h1>} {!isTokenFound && <h1> Need notification permission ❗️ </h1>}
Let’s add the getToken
function to firebase.js:
export const getToken = (setTokenFound) => { return messaging.getToken({vapidKey: 'GENERATED_MESSAGING_KEY'}).then((currentToken) => { if (currentToken) { console.log('current token for client: ', currentToken); setTokenFound(true); // Track the token -> client mapping, by sending to backend server // show on the UI that permission is secured } else { console.log('No registration token available. Request permission to generate one.'); setTokenFound(false); // shows on the UI that permission is required } }).catch((err) => { console.log('An error occurred while retrieving token. ', err); // catch error while creating client token }); }
Notice how we are passing the setTokenFound
function to the getToken
function so that we can keep a track of whether we got the client token (i.e. whether notification permission was provided) or not. When we save and execute the code now, we get a popup requesting for notifications:
The messaging.getToken
displays the UI for notification, waits for a user input and upon allowing, assigns a unique token to the client that can be seen in the console:
Configuring message listeners
Background listener
Now that we have the notification permission and a reference to the client token on the browser side, the next step is to add a listener to the incoming push notification that is directed towards the client. We do that by adding a firebase-messaging-sw.js
service-worker file in the public folder in our react app and add this 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 var firebaseConfig = { apiKey: "YOUR_API_KEY", authDomain: "YOUR_AUTH_DOMAIN", projectId: "YOUR_PROJECT_ID", storageBucket: "YOUR_STORAGE_BUCKET", messagingSenderId: "YOUR_MESSAGING_SENDER_ID", appId: "YOUR_APP_ID" }; firebase.initializeApp(firebaseConfig); // Retrieve firebase messaging const messaging = firebase.messaging(); messaging.onBackgroundMessage(function(payload) { console.log('Received background message ', payload); const notificationTitle = payload.notification.title; const notificationOptions = { body: payload.notification.body, }; self.registration.showNotification(notificationTitle, notificationOptions); });
This code will handle all the notifications that reach the application when it’s not in the foreground (visible in the current browser tab).
Foreground listener
To take care of the case where the app is active in foreground, we need to add this code to the firebase.js
file:
export const onMessageListener = () => new Promise((resolve) => { messaging.onMessage((payload) => { resolve(payload); }); });
We also need to import this in App.js
and add logic to create the notification out of the parsed payload which looks something like this:
function App() { const [show, setShow] = useState(false); const [notification, setNotification] = useState({title: '', body: ''}); const [isTokenFound, setTokenFound] = useState(false); getToken(setTokenFound); onMessageListener().then(payload => { setShow(true); setNotification({title: payload.notification.title, body: payload.notification.body}) console.log(payload); }).catch(err => console.log('failed: ', err)); return ( <div className="App"> <Toast onClose={() => setShow(false)} show={show} delay={3000} autohide animation style={{ position: 'absolute', top: 20, right: 20, minWidth: 200 }}> <Toast.Header> <img src="holder.js/20x20?text=%20" className="rounded mr-2" alt="" /> <strong className="mr-auto">{notification.title}</strong> <small>just now</small> </Toast.Header> <Toast.Body>{notification.body}</Toast.Body> </Toast> <header className="App-header"> {isTokenFound && <h1> Notification permission enabled 👍🏻 </h1>} {!isTokenFound && <h1> Need notification permission ❗️ </h1>} <img src={logo} className="App-logo" alt="logo" /> <Button onClick={() => setShow(true)}>Show Toast</Button> </header> </div> ); }
And we’re all set to receive both foreground and background notifications in our React app.
Testing push notifications
We can now test whether our notifications are functional by visiting the cloud messaging section of our app and triggering a test notification:
- Under the Notifications tab, click on New notification
- Enter the Notification title and Notification text
- Under the Device preview section, click on Send test message
- In the popup that opens, enter the client token that is logged in the console as the FCM registration token and press the + button
- Make sure that the FCM token is checked and click on “Test”
If the React app is open in a foreground browser tab, you should see the message popup with the contents that were just filled up. And in case the tab was in background, you should see a default notification which looks something like this:
Once this testing is done and the message appears as per your liking, you can continue with the process of configuring the push notification by selecting the app and creating a campaign. In that case, all the users of that app would receive a push notification similar to the one tested just now.
Conclusion
We have covered the steps involved in integrating Firebase into a React app and configuring both foreground and background messages. In this flow, we are triggering the notifications via the cloud messaging panel in Firebase. An alternate method would be to store a client-to-token mapping in a backend server and when the notification is enabled then later trigger the notification using the firebase-admin
library by targeting those particular tokens. That would allow for a more fine-grained control over notifications. But as far as sending push notifications to client side devices (iOS, Android, or web) is concerned, using the Firebase platform is preferred. Do give it a try the next time you come across such a requirement in your project. Find the full code on this GitHub repository.
Full visibility into production React apps
Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.
The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.
Modernize how you debug your React apps — start monitoring for free.