Skip to main content

React Native UI Kit Sample App

Reference implementation of React Native UI Kit, FCM and Push Notification Setup.

What this guide covers

  • CometChat dashboard setup (enable push, add FCM provider) with screenshots.
  • Firebase + React Native wiring (credentials, the push package, the Google Services plugin).
  • Wiring the package’s notification and call handlers into your app.
  • Native Android setup (Gradle, notification icon) — no manifest entries or Kotlin code to write.
  • Token registration, notification/call handling, navigation, testing, and troubleshooting.
  • App icon badge count using unreadMessageCount from the CometChat push payload.

How FCM + CometChat work together

  • FCM’s role: Issues the Android registration token and delivers the push payload to the device.
  • CometChat’s role: The FCM provider you add in the CometChat dashboard stores your Firebase service account. When CometChatPushNotifications.init() runs after login, the package registers the token for the logged-in user, and CometChat sends pushes to FCM on your behalf.
  • The package’s role: Its own FirebaseMessagingService receives each push and shows the chat notification or the full-screen incoming call. Every CometChat action — registering the token, accepting or rejecting a call — runs in JavaScript through the Chat SDK your app already uses.
  • Flow: Permission (Android 13+ POST_NOTIFICATIONS) → Firebase returns the FCM token → after login, init() registers it with AppCredentials.fcmProviderId → CometChat sends to FCM → FCM delivers to the device → the package shows the notification or call → your onNotificationTap, onCallAccepted and onCallEnded handlers navigate.

1. Enable push and add providers (CometChat Dashboard)

  1. Go to Notifications → Settings and enable Push Notifications.
Enable Push Notifications
  1. Click Add Credentials, choose FCM, upload the Firebase service account JSON (Firebase → Project settings → Service accounts → Generate new private key), and copy the Provider ID.
Upload FCM service account JSON
Keep the provider ID—you’ll use it in AppCredentials.fcmProviderId.

2. Prepare Firebase and credentials

2.1 Firebase Console

  1. Register your Android package name (the same as applicationId in android/app/build.gradle) and download google-services.json into android/app.
  2. Enable Cloud Messaging.
Firebase - Push Notifications

2.2 Local configuration file

Create src/AppCredentials.ts with your app credentials and provider IDs. The same file serves the iOS guide:
src/AppCredentials.ts

3. Bring the push package into React Native

3.1 Install the package

Remove other push and call libraries first@notifee/react-native, react-native-callkeep, react-native-voip-push-notification — along with their code and native setup, or every notification or call arrives twice. If you keep @react-native-firebase/messaging for other features, follow step 4.5.

3.2 Wire the entry points

The JavaScript below is the same for Android and iOS — one set of files serves both guides. Lines for one platform do nothing on the other: registerBackgroundCallTask() and notificationSmallIcon only apply on Android, and waiting for each permission answer before the next request matters only on Android.
index.js — register the package’s background task at module scope. It lets a fully killed app reject a call declined from its notification: the package re-initializes the Chat SDK with the settings init() saved, and rejects the call.
index.js
To add your own logic, pass a handler — it runs after the package has rejected the call:
src/navigation/navigationRef.ts — a notification tap or answered call that launched the app arrives before your navigator exists, so every navigation waits for it:
src/navigation/navigationRef.ts
src/push/pushNotifications.ts — everything push does for the logged-in user: the tap, call-accepted and call-ended handlers, the permission requests, and init():
src/push/pushNotifications.ts
Not using the UI Kit? Delete the @cometchat/chat-uikit-react-native import and the emitMessageEvent block in markConversationRead, point SCREENS and the route params at your own screens, and in logout.ts call CometChat.logout() instead of CometChatUIKit.logout().
App.tsx — call usePushOnLogin() once, in a component that renders after CometChat is initialized, and pass navigationRef to your NavigationContainer:
App.tsx
usePushOnLogin() starts push after a fresh login and when a session is restored on launch, and removes the handlers when the user logs out — so a later login never registers them twice.

3.3 Align dependencies and configuration

  • Peer dependencies: @cometchat/chat-sdk-react-native (or the UI Kit) for chat, @cometchat/calls-sdk-react-native for calls, and React Navigation for the handlers above.
  • init() options:
    • fcmProviderId (Android) and apnsProviderId (iOS) — from step 1.
    • notificationSmallIcon — the Android status-bar icon.
    • showInForeground (default false) — show chat notifications while the app is open.
    • ringInForeground (default true) — ring with the system call UI while the app is open. Set false when your app shows its own incoming-call screen, as the UI Kit does.
    • voip (default true), androidChannelId, androidChannelName.

4. Configure the native Android layer

4.1 Gradle + Firebase

  1. Add google-services.json to android/app.
  2. Apply the Google Services plugin. The package already depends on firebase-messaging, so don’t add it yourself:
android/build.gradle
android/app/build.gradle
Keep minSdkVersion 24 or higher.

4.2 Manifest permissions and components

You don’t edit AndroidManifest.xml. The package’s manifest is merged into your app with everything push and calls need:
  • Components: its FirebaseMessagingService, the incoming-call foreground service, the full-screen CallRingingActivity (shows over the lock screen), the call action receiver, and the background task service for killed-app declines.
  • Permissions: POST_NOTIFICATIONS, USE_FULL_SCREEN_INTENT, FOREGROUND_SERVICE, FOREGROUND_SERVICE_PHONE_CALL, MANAGE_OWN_CALLS, WAKE_LOCK, VIBRATE, RECORD_AUDIO, CAMERA and BLUETOOTH_CONNECT. Camera and microphone hardware are declared optional, so Google Play doesn’t hide your app from devices without them.
Google Play (Android 14+): apps with calls complete two declarations in Play Console under App contentFull-screen intent permission (calling as core functionality) and Foreground service permissions (the Phone call type). Without the first, Play revokes USE_FULL_SCREEN_INTENT and calls ring only as a heads-up notification.
Chat-only apps remove the call permissions and components (with xmlns:tools="http://schemas.android.com/tools" on the manifest element), and skip requestCallPermissions() and registerBackgroundCallTask():
android/app/src/main/AndroidManifest.xml
tools:node="remove" drops an entry whichever library declared it — keep any permission another part of your app still uses.

4.3 Notification icon

Add a white-on-transparent drawable named ic_notification — for example android/app/src/main/res/drawable/ic_notification.png. Android Studio generates one: right-click resNew → Image Asset, icon type Notification Icons, name ic_notification. Without it, the package falls back to your launcher icon, which the status bar draws as a plain white shape. There is no Kotlin bridge to write: the ringing screen, the Answer and Decline actions, and the killed-app decline are built into the package.

4.4 OEM permissions for lock-screen calls

Stock Android shows the full-screen call over the lock screen out of the box. OEM skins (MIUI/Redmi/POCO, Oppo, Vivo) gate background-launched full-screen activities behind their own toggles — without them, a locked or killed call shows only a heads-up notification, and the ringing screen appears after unlock. Guide users to grant, on those devices:
  • Autostart — Settings → Apps → your app → Autostart (or the Security app).
  • Display pop-up windows while running in background — Settings → Apps → your app → Other permissions.
  • Show on lock screen — the same “Other permissions” screen.
  • Disable battery optimization for the app.
These settings can’t be granted programmatically; open the app’s settings page so the user can toggle them:

4.5 Apps that also use @react-native-firebase/messaging

Android delivers FCM messages and token refreshes to only one FirebaseMessagingService, and React Native Firebase ships its own — so with both installed, one of them silently receives nothing. Replace both with a service of your own that forwards to each:
  1. Remove both library services and register yours:
android/app/src/main/AndroidManifest.xml
  1. Add Firebase Messaging to your app so the service can extend it (use the BOM version your other Firebase libraries use):
android/app/build.gradle
  1. Add the service next to MainApplication.kt:
AppMessagingService.kt
  1. React Native Firebase also hands every push to its JavaScript handlers. Skip CometChat’s there, in onMessage and setBackgroundMessageHandler:

5. Token registration and runtime events

5.1 FCM tokens

The package fetches the FCM token during init() and registers it with your FCM provider for the logged-in user; when Firebase refreshes the token, it registers the new one. If init() runs a moment before login finishes, registration retries 5 times, 3 seconds apart. setupPushOnLogin() requests the permissions, in order, before init(). To check or request them elsewhere:
Always await one permission request before starting the next. Android allows only one pending request per activity: a second request cancels the dialog still on screen, and the OS reports it as denied without the user seeing it. On a fresh install that leaves the app with no notification permission — pushes arrive and are dropped.
requestNotificationPermission() resolves true or false on the user’s answer. It rejects when the permission could not be requested at all — a different situation from the user declining, and not a reason to skip init(): You rarely need it, but you can register a token yourself:

5.2 Local notifications and navigation

  • App in the background or killed: the package’s service shows the notification with your ic_notification icon.
  • App open: the payload goes to onMessageReceived; a system notification also shows when showInForeground is true.
  • Tap: onNotificationTap fires, and openFromNotification marks the conversation read, then opens the thread for a thread reply, otherwise the conversation. A tap that launched the app is held until your handler subscribes, and navigation waits for the navigator.

5.3 Call events

5.4 Unregister on logout

Add src/push/logout.ts and call it from your logout button instead of logging out directly:
src/push/logout.ts
unregister() must run before logout. It needs the session’s auth token — after logout it fails, and the device keeps receiving notifications for the user who just logged out.

6. Badge count

CometChat’s Enhanced Push Notification payload includes an unreadMessageCount field (a string) representing the total unread messages across all conversations for the logged-in user. You can use it to set a launcher badge.

6.1 Enable unread badge count on the CometChat Dashboard

  1. Go to CometChat Dashboard → Notifications → Settings → Preferences → Push Notification Preferences.
  2. Scroll to the bottom and enable the Unread Badge Count toggle.
This ensures CometChat includes the unreadMessageCount field in every push payload sent to your app.

6.2 Expected payload format

CometChat sends FCM data messages with this structure (relevant fields):
unreadMessageCount is a string; the package hands every payload value to JavaScript as a string, so convert it with Number() before use.

6.3 Update the app badge from the push payload

Android has no OS-level app icon badge API, and the push package doesn’t manage launcher badges. While the app is open, onMessageReceived receives each payload — hand unreadMessageCount to a launcher-badge library. In the background the package shows the notification, and Android’s notification dot marks the app icon.

7. Testing checklist

Use physical devices and a release build: a debug build loads its JavaScript from Metro, which delays the first JavaScript that runs in a killed app.
  1. Fresh install: install, log in, and confirm the notification prompt waits for your answer before the microphone/camera prompt appears. Then send a message from another user — it must arrive.
  2. Chat notifications:
    • App open: exactly one notification (showInForeground: true).
    • App in the background: a notification appears; tapping it opens the conversation.
    • App killed: tapping the notification starts the app in the conversation.
    • A thread reply opens the thread; a group message opens the group.
  3. Calls, app killed, phone locked:
    • The full-screen ringing screen shows with Accept and Decline.
    • Accept connects the call with audio both ways.
    • Decline shows the call as rejected on the caller’s side.
    • The caller cancelling stops the ring.
  4. Calls, app in the background: the ringing screen shows, and accept and decline both work.
  5. Calls, app open: only your in-app incoming-call screen rings (ringInForeground: false).
  6. Logout: log out, send a message from another user — nothing arrives. Log in as another user — only that user’s notifications arrive.
  7. OEM devices (MIUI, Oppo, Vivo): grant the step 4.4 permissions and re-check locked and killed calls.

8. Troubleshooting tips