Skip to content
capacitor-push-notifications logo

Push Notifications in Capacitor

capacitor-push-notifications

Complete guide to implementing push notifications in Capacitor apps using Firebase Cloud Messaging (FCM) and Apple Push Notification Service (APNs). Covers setup, handling, and best practices. Use this skill when users need to add push notifications.

SKILL.md

Full skill instructions

Push Notifications in Capacitor

Implement push notifications for iOS and Android using Firebase and APNs.

When to Use This Skill

  • User wants push notifications
  • User needs FCM setup
  • User asks about APNs
  • User has notification issues
  • User wants rich notifications

Quick Start

Install Plugin

bun add @capacitor/​push-notifications
bunx cap sync

Basic Implementation

import { PushNotifications } from '@capacitor/​push-notifications';

async function initPushNotifications() {
  // Request permission
  const permission = await PushNotifications.requestPermissions();

  if (permission.receive === 'granted') {
    // Register for push
    await PushNotifications.register();
  }

  // Get FCM token
  PushNotifications.addListener('registration', (token) => {
    console.log('Push token:', token.value);
    // Send token to your server
    sendTokenToServer(token.value);
  });

  // Handle registration error
  PushNotifications.addListener('registrationError', (error) => {
    console.error('Registration error:', error);
  });

  // Handle incoming notification (foreground)
  PushNotifications.addListener('pushNotificationReceived', (notification) => {
    console.log('Notification received:', notification);
    // Show in-app notification
    showInAppNotification(notification);
  });

  // Handle notification tap
  PushNotifications.addListener('pushNotificationActionPerformed', (action) => {
    console.log('Notification action:', action);
    // Navigate based on notification data
    handleNotificationTap(action.notification);
  });
}

Firebase Setup

1. Create Firebase Project

  1. Go to https://console.firebase.google.com
  2. Create new project
  3. Add iOS and Android apps

2. Android Configuration

Download google-services.json to android/​app/

// android/​build.gradle
buildscript {
    dependencies {
        classpath 'com.google.gms:google-services:4.4.0'
    }
}
// android/​app/​build.gradle
apply plugin: 'com.google.gms.google-services'

dependencies {
    implementation platform('com.google.firebase:firebase-bom:32.7.0')
    implementation 'com.google.firebase:firebase-messaging'
}

3. iOS Configuration

Download GoogleService-Info.plist to ios/​App/​App/

# ios/​App/​Podfile
pod 'Firebase/​Messaging'
// ios/​App/​App/​AppDelegate.swift
import Firebase
import FirebaseMessaging

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        FirebaseApp.configure()
        return true
    }

    func application(
        _ application: UIApplication,
        didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
    ) {
        Messaging.messaging().apnsToken = deviceToken
    }
}

4. iOS Capabilities

In Xcode:

  1. Select App target
  2. Signing & Capabilities
  3. Add "Push Notifications"
  4. Add "Background Modes" > "Remote notifications"

APNs Key Setup (iOS)

Create APNs Key

  1. Go to https://developer.apple.com/account
  2. Certificates, IDs & Profiles
  3. Keys > Create Key
  4. Enable Apple Push Notifications service (APNs)
  5. Download .p8 file

Add to Firebase

  1. Firebase Console > Project Settings
  2. Cloud Messaging tab
  3. iOS app configuration
  4. Upload APNs Authentication Key (.p8)
  5. Enter Key ID and Team ID

Sending Notifications

Firebase Admin SDK (Node.js)

import admin from 'firebase-admin';

// Initialize
admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
});

// Send to single device
async function sendToDevice(token: string) {
  await admin.messaging().send({
    token,
    notification: {
      title: 'Hello!',
      body: 'You have a new message',
    },
    data: {
      type: 'message',
      messageId: '123',
    },
    android: {
      priority: 'high',
      notification: {
        channelId: 'messages',
        icon: 'ic_notification',
        color: '#4285f4',
      },
    },
    apns: {
      payload: {
        aps: {
          badge: 1,
          sound: 'default',
        },
      },
    },
  });
}

// Send to topic
async function sendToTopic(topic: string) {
  await admin.messaging().send({
    topic,
    notification: {
      title: 'Breaking News',
      body: 'Something important happened',
    },
  });
}

// Send to multiple devices
async function sendToMultiple(tokens: string[]) {
  await admin.messaging().sendEachForMulticast({
    tokens,
    notification: {
      title: 'Update',
      body: 'New features available',
    },
  });
}

HTTP v1 API

curl -X POST \
  'https://fcm.googleapis.com/​v1/​projects/​YOUR_PROJECT/​messages:send' \
  -H 'Authorization: Bearer ACCESS_TOKEN' \
  -H 'Content-Type: application/​json' \
  -d '{
    "message": {
      "token": "DEVICE_TOKEN",
      "notification": {
        "title": "Hello",
        "body": "World"
      }
    }
  }'

Advanced Features

Notification Channels (Android)

import { PushNotifications } from '@capacitor/​push-notifications';

// Create channel
await PushNotifications.createChannel({
  id: 'messages',
  name: 'Messages',
  description: 'Message notifications',
  importance: 5, // Max importance
  visibility: 1, // Public
  sound: 'notification.wav',
  vibration: true,
  lights: true,
  lightColor: '#FF0000',
});

// Delete channel
await PushNotifications.deleteChannel({ id: 'old-channel' });

// List channels
const channels = await PushNotifications.listChannels();

Topic Subscription

// Subscribe to topic
await PushNotifications.addListener('registration', async () => {
  // Subscribe to topics based on user preferences
  const messaging = getMessaging();
  await subscribeToTopic(messaging, 'news');
  await subscribeToTopic(messaging, 'promotions');
});

Rich Notifications (iOS)

// ios/​App/​NotificationService/​NotificationService.swift
import UserNotifications

class NotificationService: UNNotificationServiceExtension {
    override func didReceive(
        _ request: UNNotificationRequest,
        withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
    ) {
        guard let mutableContent = request.content.mutableCopy() as? UNMutableNotificationContent else {
            contentHandler(request.content)
            return
        }

        // Add image
        if let imageUrl = request.content.userInfo["image"] as? String,
           let url = URL(string: imageUrl) {
            downloadImage(url: url) { attachment in
                if let attachment = attachment {
                    mutableContent.attachments = [attachment]
                }
                contentHandler(mutableContent)
            }
        } else {
            contentHandler(mutableContent)
        }
    }
}

Notification Actions

// Handle action buttons
PushNotifications.addListener('pushNotificationActionPerformed', (action) => {
  switch (action.actionId) {
    case 'reply':
      // Handle reply action
      const input = action.inputValue;
      sendReply(input);
      break;
    case 'dismiss':
      // Handle dismiss
      break;
    default:
      // Handle tap
      navigateToContent(action.notification.data);
  }
});

Background Handling

Data-Only Notifications

// Server-side: Send data-only message
{
  "to": "DEVICE_TOKEN",
  "data": {
    "type": "sync",
    "action": "refresh"
  }
  // No "notification" key = data-only
}
// android/​app/​src/​main/​java/​.../​FirebaseService.kt
class FirebaseService : FirebaseMessagingService() {
    override fun onMessageReceived(message: RemoteMessage) {
        // Handle data message in background
        message.data["type"]?.let { type ->
            when (type) {
                "sync" -> performBackgroundSync()
                "update" -> checkForUpdates()
            }
        }
    }
}

Local Notifications Fallback

import { LocalNotifications } from '@capacitor/​local-notifications';

// Show local notification when in foreground
PushNotifications.addListener('pushNotificationReceived', async (notification) => {
  await LocalNotifications.schedule({
    notifications: [
      {
        id: Date.now(),
        title: notification.title || '',
        body: notification.body || '',
        extra: notification.data,
      },
    ],
  });
});

Best Practices

Permission Handling

async function requestNotificationPermission() {
  const { receive } = await PushNotifications.checkPermissions();

  if (receive === 'prompt') {
    // Show explanation first
    const shouldRequest = await showPermissionExplanation();

    if (shouldRequest) {
      const result = await PushNotifications.requestPermissions();
      return result.receive === 'granted';
    }
    return false;
  }

  if (receive === 'denied') {
    // Guide user to settings
    showSettingsPrompt();
    return false;
  }

  return receive === 'granted';
}

Token Refresh

// Handle token refresh
PushNotifications.addListener('registration', async (token) => {
  const oldToken = await getStoredToken();

  if (oldToken !== token.value) {
    // Token changed, update server
    await updateServerToken(oldToken, token.value);
    await storeToken(token.value);
  }
});

Error Handling

PushNotifications.addListener('registrationError', (error) => {
  console.error('Push registration failed:', error);

  // Log to analytics
  analytics.logEvent('push_registration_failed', {
    error: error.error,
  });

  // Retry with backoff
  scheduleRetry();
});

Troubleshooting

iOS Not Receiving

  1. Check APNs key in Firebase
  2. Verify Push Notifications capability
  3. Check provisioning profile
  4. Verify device token format
  5. Test with Firebase Console

Android Not Receiving

  1. Verify google-services.json
  2. Check notification channel exists
  3. Verify FCM token
  4. Check battery optimization
  5. Test with Firebase Console

Common Issues

IssueSolution
No tokenCheck permissions, network
Foreground onlyImplement background handler
Delayed deliveryUse high priority, data-only
No soundConfigure notification channel
Badge not updatingSet badge in payload

Resources

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View