Skip to main content
RapidDev - Software Development Agency
firebase-tutorial

How to Set Up Firebase Cloud Messaging (FCM)

To set up Firebase Cloud Messaging (FCM) in a web app, register a Firebase project, add a service worker file to handle background messages, request notification permission from the user with Notification.requestPermission(), retrieve the FCM registration token using getToken(), and listen for incoming messages with onMessage(). The service worker file firebase-messaging-sw.js runs in the background and displays notifications when the app is not in focus.

What you'll learn

  • How to configure FCM in the Firebase Console and generate a VAPID key
  • How to request notification permission and retrieve the FCM token
  • How to set up a service worker for background notifications
  • How to handle foreground messages with onMessage()
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner8 min read15-20 minFirebase (Blaze and Spark plans), firebase v9+ modular SDKMarch 2026RapidDev Engineering Team
TL;DR

To set up Firebase Cloud Messaging (FCM) in a web app, register a Firebase project, add a service worker file to handle background messages, request notification permission from the user with Notification.requestPermission(), retrieve the FCM registration token using getToken(), and listen for incoming messages with onMessage(). The service worker file firebase-messaging-sw.js runs in the background and displays notifications when the app is not in focus.

Setting Up Firebase Cloud Messaging for Web Push Notifications

Firebase Cloud Messaging (FCM) lets you send push notifications to users even when your web app is closed. This tutorial covers the complete setup from Firebase Console configuration through to receiving and displaying notifications in both foreground and background states. You will create a service worker, request user permission, obtain a device token, and handle incoming messages.

Prerequisites

  • A Firebase project created in the Firebase Console
  • Firebase SDK installed in your project (npm install firebase)
  • A web app served over HTTPS (required for service workers and push notifications)
  • Basic understanding of JavaScript Promises and async/await

Step-by-step guide

1

Generate a VAPID key in the Firebase Console

Navigate to your Firebase project in the Firebase Console. Go to Project Settings (gear icon) then Cloud Messaging tab. Scroll down to the Web configuration section and click Generate key pair. This creates a VAPID (Voluntary Application Server Identification) key that authenticates your app with the FCM service. Copy this key because you will use it when requesting the FCM token in your client code.

Expected result: A VAPID key pair appears in the Cloud Messaging settings. Copy the public key for use in your app code.

2

Initialize Firebase Messaging in your app

Import and initialize the Firebase app and messaging modules using the modular SDK v9+ syntax. The getMessaging() function returns the messaging instance that you will use to request tokens and listen for messages. Place this initialization code in a dedicated file so you can import the messaging instance throughout your app.

typescript
1// src/firebase.ts
2import { initializeApp } from 'firebase/app';
3import { getMessaging } from 'firebase/messaging';
4
5const firebaseConfig = {
6 apiKey: 'YOUR_API_KEY',
7 authDomain: 'YOUR_PROJECT.firebaseapp.com',
8 projectId: 'YOUR_PROJECT_ID',
9 storageBucket: 'YOUR_PROJECT.appspot.com',
10 messagingSenderId: 'YOUR_SENDER_ID',
11 appId: 'YOUR_APP_ID'
12};
13
14const app = initializeApp(firebaseConfig);
15export const messaging = getMessaging(app);

Expected result: Firebase app and messaging are initialized and the messaging instance is exported for use in other files.

3

Create the service worker file for background messages

Create a file named firebase-messaging-sw.js in your public directory (the root of your served files). This service worker runs in the background and handles push notifications when the user is not actively viewing your app. It must import the Firebase messaging compat libraries and call onBackgroundMessage() to display notifications. The file must be named exactly firebase-messaging-sw.js and served from the root path.

typescript
1// public/firebase-messaging-sw.js
2importScripts('https://www.gstatic.com/firebasejs/10.12.0/firebase-app-compat.js');
3importScripts('https://www.gstatic.com/firebasejs/10.12.0/firebase-messaging-compat.js');
4
5firebase.initializeApp({
6 apiKey: 'YOUR_API_KEY',
7 authDomain: 'YOUR_PROJECT.firebaseapp.com',
8 projectId: 'YOUR_PROJECT_ID',
9 storageBucket: 'YOUR_PROJECT.appspot.com',
10 messagingSenderId: 'YOUR_SENDER_ID',
11 appId: 'YOUR_APP_ID'
12});
13
14const messaging = firebase.messaging();
15
16messaging.onBackgroundMessage((payload) => {
17 const notificationTitle = payload.notification?.title || 'New notification';
18 const notificationOptions = {
19 body: payload.notification?.body || '',
20 icon: '/icon-192x192.png'
21 };
22 self.registration.showNotification(notificationTitle, notificationOptions);
23});

Expected result: The firebase-messaging-sw.js file is served at the root of your domain and handles background push notifications.

4

Request notification permission and get the FCM token

Before you can send push notifications, the user must grant permission. Call Notification.requestPermission() to show the browser permission prompt. If granted, call getToken() with your VAPID key to get the unique FCM registration token for this browser on this device. Send this token to your backend server so you can target this user with push messages. The token can change over time, so call getToken() on each app load and update your server if it changes.

typescript
1// src/notifications.ts
2import { getToken, onMessage } from 'firebase/messaging';
3import { messaging } from './firebase';
4
5const VAPID_KEY = 'YOUR_VAPID_PUBLIC_KEY';
6
7export async function requestNotificationPermission(): Promise<string | null> {
8 const permission = await Notification.requestPermission();
9
10 if (permission !== 'granted') {
11 console.warn('Notification permission denied');
12 return null;
13 }
14
15 const token = await getToken(messaging, { vapidKey: VAPID_KEY });
16 console.log('FCM Token:', token);
17
18 // Send this token to your backend to store for this user
19 // await saveTokenToServer(token);
20
21 return token;
22}

Expected result: The browser shows a notification permission prompt. If granted, an FCM token string is returned and logged to the console.

5

Handle foreground messages with onMessage()

When the user is actively viewing your app, push messages do not automatically show a browser notification. Instead, FCM delivers them to the onMessage() callback where you can display a custom in-app notification, update a badge count, or show a toast. Set up this listener in your app initialization code so it runs as long as the app is open.

typescript
1// src/notifications.ts (continued)
2export function listenForMessages(): void {
3 onMessage(messaging, (payload) => {
4 console.log('Foreground message received:', payload);
5
6 const title = payload.notification?.title || 'Notification';
7 const body = payload.notification?.body || '';
8
9 // Option 1: Show a browser notification manually
10 if (Notification.permission === 'granted') {
11 new Notification(title, { body, icon: '/icon-192x192.png' });
12 }
13
14 // Option 2: Show an in-app toast/alert
15 // showToast({ title, body });
16 });
17}

Expected result: When a push message arrives while the app is in the foreground, onMessage fires and you can display the notification content however you choose.

6

Send a test notification from the Firebase Console

Go to the Firebase Console, navigate to Messaging (under Engage), and click New campaign then Notifications. Enter a notification title and body text. Under Target, select your app. Under Scheduling, select Send now. Click Review then Publish. The notification should appear in your browser within a few seconds. If your app is in the foreground, the onMessage callback fires. If the app is in the background or closed, the service worker displays the notification.

Expected result: A push notification appears in your browser, confirming the complete FCM setup is working end to end.

Complete working example

fcm-setup.ts
1// Complete Firebase Cloud Messaging setup for web
2// Includes initialization, permission request, token retrieval, and message handling
3
4import { initializeApp } from 'firebase/app';
5import { getMessaging, getToken, onMessage } from 'firebase/messaging';
6
7const firebaseConfig = {
8 apiKey: 'YOUR_API_KEY',
9 authDomain: 'YOUR_PROJECT.firebaseapp.com',
10 projectId: 'YOUR_PROJECT_ID',
11 storageBucket: 'YOUR_PROJECT.appspot.com',
12 messagingSenderId: 'YOUR_SENDER_ID',
13 appId: 'YOUR_APP_ID'
14};
15
16const app = initializeApp(firebaseConfig);
17const messaging = getMessaging(app);
18
19const VAPID_KEY = 'YOUR_VAPID_PUBLIC_KEY';
20
21// Request permission and retrieve the FCM token
22export async function initializePushNotifications(): Promise<string | null> {
23 try {
24 const permission = await Notification.requestPermission();
25
26 if (permission !== 'granted') {
27 console.warn('Notification permission was not granted');
28 return null;
29 }
30
31 const token = await getToken(messaging, { vapidKey: VAPID_KEY });
32 console.log('FCM registration token:', token);
33
34 // Store token on your server for this user
35 // await fetch('/api/save-fcm-token', {
36 // method: 'POST',
37 // headers: { 'Content-Type': 'application/json' },
38 // body: JSON.stringify({ token })
39 // });
40
41 return token;
42 } catch (error) {
43 console.error('Failed to initialize push notifications:', error);
44 return null;
45 }
46}
47
48// Listen for messages when the app is in the foreground
49export function onForegroundMessage(
50 callback: (title: string, body: string) => void
51): void {
52 onMessage(messaging, (payload) => {
53 const title = payload.notification?.title || 'Notification';
54 const body = payload.notification?.body || '';
55 callback(title, body);
56 });
57}
58
59// Check if notifications are supported and permitted
60export function getNotificationStatus(): 'granted' | 'denied' | 'default' | 'unsupported' {
61 if (!('Notification' in window)) {
62 return 'unsupported';
63 }
64 return Notification.permission;
65}

Common mistakes when setting up Firebase Cloud Messaging (FCM)

Why it's a problem: Placing firebase-messaging-sw.js inside a subdirectory instead of the public root, causing service worker registration to fail

How to avoid: The service worker file must be served at the root path of your domain (e.g., https://yourapp.com/firebase-messaging-sw.js). Place it in the public/ directory of your project.

Why it's a problem: Calling Notification.requestPermission() on page load without user interaction, causing browsers to silently block the request

How to avoid: Only request permission after a user action such as clicking an Enable Notifications button. Modern browsers require a user gesture for notification permission prompts.

Why it's a problem: Using the modular SDK v9 import syntax inside the service worker file instead of compat imports

How to avoid: Service workers use importScripts() which only supports the compat (v8-style) Firebase SDK. Use firebase-app-compat.js and firebase-messaging-compat.js in the service worker.

Why it's a problem: Not updating the stored FCM token when it changes, causing notifications to stop working

How to avoid: Call getToken() on every app load and compare the result with the stored token. If it differs, update the token on your server.

Best practices

  • Request notification permission only after a clear user action, never on initial page load
  • Store FCM tokens on your backend server and update them on every app load in case they change
  • Use data messages instead of notification messages when you need full control over display in both foreground and background
  • Keep the service worker Firebase SDK version in sync with your main app SDK version
  • Provide an in-app way for users to opt out of notifications by deleting their token with deleteToken()
  • Test notifications in both foreground and background states during development
  • Handle the case where the browser does not support the Notifications API or service workers

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I want to add push notifications to my React web app using Firebase Cloud Messaging. Show me the complete setup including the service worker file, VAPID key configuration, permission request, token retrieval, and handling both foreground and background messages.

Firebase Prompt

Set up Firebase Cloud Messaging in a TypeScript web app with the modular v9+ SDK. Include the firebase-messaging-sw.js service worker for background notifications, getToken() with VAPID key, onMessage() for foreground handling, and a function to save the token to the server.

Frequently asked questions

Does Firebase Cloud Messaging work on the Spark (free) plan?

Yes. FCM is completely free on both Spark and Blaze plans with no message volume limits. You only pay if you use Cloud Functions to send messages programmatically.

Why is getToken() returning an error about service worker registration?

The firebase-messaging-sw.js file must be served at the root of your domain. Ensure it is in your public/ directory and accessible at https://yourdomain.com/firebase-messaging-sw.js. Also verify your site is served over HTTPS, as service workers require a secure context.

Can I send FCM messages from the client side?

No. Sending messages requires the Firebase Admin SDK which runs on a server. The client side only receives messages. Use Cloud Functions, your own backend, or the Firebase Console to send messages.

How long does an FCM token last?

FCM tokens do not have a fixed expiration but can change when the user clears browser data, the service worker is updated, or the app is reinstalled. Call getToken() on every app load and update your server when the token changes.

Why are notifications not showing when the app is in the foreground?

By design, FCM does not display browser notifications when the app is in the foreground. Instead, it delivers the payload to the onMessage() callback. You must handle the display yourself, either by showing an in-app toast or manually creating a Notification object.

Can I customize the notification icon and click action?

Yes. In the service worker's onBackgroundMessage callback, pass icon, badge, image, and data with a click_action URL to the showNotification options. For foreground messages, you have full control in the onMessage handler.

Can RapidDev help implement a complete notification system with Firebase?

Yes. RapidDev can build end-to-end notification systems including FCM setup, server-side sending logic, topic management, notification preferences, and delivery tracking.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.