DEV Community

Cover image for Firebase Cloud Messaging in 2026: Web Push Notifications with Vue 3 & SDK v10
Pooya Golchian
Pooya Golchian

Posted on • Originally published at pooya.blog

Firebase Cloud Messaging in 2026: Web Push Notifications with Vue 3 & SDK v10

Firebase Cloud Messaging (FCM) is the standard for delivering web push notifications. The legacy CDN-based setup (Firebase v7) is deprecated. This guide uses the v10 modular SDK with Vue 3, smaller bundles, full TypeScript support, and the correct service worker pattern for 2026.

Project Setup

pnpm add firebase
# Or: npm install firebase
Enter fullscreen mode Exit fullscreen mode

Create src/lib/firebase.ts:


const firebaseConfig = {
  apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
  authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
  projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
  storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
  appId: import.meta.env.VITE_FIREBASE_APP_ID,
};

export const app = initializeApp(firebaseConfig);
export const messaging = getMessaging(app);
Enter fullscreen mode Exit fullscreen mode

Store all values in .env.local, never hardcode Firebase credentials in source.

Step 1: Get a VAPID Key

  1. Firebase Console → Project Settings → Cloud Messaging
  2. Under Web configuration, click Generate key pair
  3. Copy the public key, you’ll use it as VITE_FIREBASE_VAPID_KEY

Step 2: Request Permission & Get FCM Token (Vue 3 Composable)

Create src/composables/usePushNotifications.ts:


export function usePushNotifications() {
  const token = ref<string | null>(null);
  const notification = ref<MessagePayload | null>(null);
  const error = ref<string | null>(null);

  async function requestPermissionAndGetToken() {
    try {
      const permission = await Notification.requestPermission();
      if (permission !== 'granted') {
        error.value = 'Notification permission denied';
        return;
      }

      // Register the service worker first
      const registration = await navigator.serviceWorker.register(
        '/firebase-messaging-sw.js'
      );

      token.value = await getToken(messaging, {
        vapidKey: import.meta.env.VITE_FIREBASE_VAPID_KEY,
        serviceWorkerRegistration: registration,
      });

      console.log('FCM token:', token.value);
      // TODO: send token.value to your backend for storage

    } catch (err) {
      error.value = err instanceof Error ? err.message : 'Unknown error';
    }
  }

  // Handle foreground messages (when the app tab is open)
  function listenForMessages() {
    onMessage(messaging, (payload) => {
      console.log('Foreground message received:', payload);
      notification.value = payload;
    });
  }

  return { token, notification, error, requestPermissionAndGetToken, listenForMessages };
}
Enter fullscreen mode Exit fullscreen mode

Use it in a component:

<script setup lang="ts">

const { token, notification, error, requestPermissionAndGetToken, listenForMessages } =
  usePushNotifications();

onMounted(() => {
  listenForMessages();
});
</script>

<template>
  <div>
    <button @click="requestPermissionAndGetToken">Enable Notifications</button>
    <p v-if="error" class="text-red-500">{{ error }}</p>
    <p v-if="notification">{{ notification.notification?.title }}</p>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Step 3: Create the Service Worker

Create public/firebase-messaging-sw.js (must be at the web root):

// public/firebase-messaging-sw.js

import {
  getMessaging,
  onBackgroundMessage,
} from 'https://www.gstatic.com/firebasejs/10.12.0/firebase-messaging-sw.js';

const 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',
};

const app = initializeApp(firebaseConfig);
const messaging = getMessaging(app);

onBackgroundMessage(messaging, (payload) => {
  console.log('Background message received:', payload);

  const { title = 'New message', body = '' } =
    payload.notification ?? {};

  self.registration.showNotification(title, {
    body,
    icon: '/icon-192x192.png',
    badge: '/badge-72x72.png',
    data: payload.data,
  });
});
Enter fullscreen mode Exit fullscreen mode

Note: Service workers cannot use Vite environment variables. Inline the Firebase config here directly or use a build step to inject them at deploy time.

Step 4: Send a Notification from Node.js (Backend)

Install the Admin SDK:

npm install firebase-admin
Enter fullscreen mode Exit fullscreen mode
// server/send-notification.ts

initializeApp({
  credential: cert('./service-account.json'),
});

const messaging = getMessaging();

async function sendPushToUser(fcmToken: string) {
  const messageId = await messaging.send({
    token: fcmToken,
    notification: {
      title: 'Your order shipped! 📦',
      body: 'Estimated delivery: tomorrow between 2–5 PM',
    },
    webpush: {
      fcmOptions: {
        link: 'https://yourapp.com/orders',
      },
    },
  });

  console.log('Sent message:', messageId);
}
Enter fullscreen mode Exit fullscreen mode

For sending to a topic or user segment, use messaging.sendEachForMulticast() with an array of tokens.

Update manifest.json

Add the FCM sender ID to your web app manifest:

{
  "name": "My App",
  "gcm_sender_id": "YOUR_MESSAGING_SENDER_ID"
}
Enter fullscreen mode Exit fullscreen mode

Summary

Step What it does
initializeApp() Connects to your Firebase project
Notification.requestPermission() Prompts the browser permission dialog
getToken(messaging, { vapidKey }) Returns the FCM registration token
onMessage() Handles foreground notifications
firebase-messaging-sw.js Handles background/closed-tab notifications
messaging.send() (Admin SDK) Sends notifications from your server

The token returned by getToken() is what your server sends to. Store it per-user in your database and refresh it whenever the user revisits the app.

Top comments (0)