@felipe-software/react-native-notify-kit
v10.4.1
Published
Maintained Notifee-compatible React Native notifications library for Android, iOS, FCM Mode, rich notifications, foreground services, and Expo CNG development builds.
Maintainers
Readme
react-native-notify-kit
Maintained Notifee-compatible fork: a feature-rich React Native notification library for Android, iOS, FCM Mode, and Expo CNG development builds.
📘 Full documentation: docs.page/marcocrupi/react-native-notify-kit - public API reference, platform guides, FCM Mode, Expo CNG setup, server SDK, config plugin, and the
init-nseCLI.
An actively maintained fork of Notifee for React Native notifications, continued and improved by Marco Crupi.
This repository preserves the original Notifee APIs and native core while continuing development for modern React Native releases.
Why this fork
The original Notifee repository was officially archived by Invertase on April 7, 2026 (last release: v9.1.8, December 2024). The archived README recommends this fork (react-native-notify-kit) as a community-maintained drop-in replacement, alongside expo-notifications. Previously, in issue #1254, the Invertase maintainer had already suggested migrating to expo-notifications.
However, expo-notifications does not cover several advanced capabilities that many production apps rely on:
- Android foreground services (ongoing notifications for background tasks)
- Rich notification styles (BigPicture, Messaging, Inbox)
- Progress bar notifications
- Full-screen intent notifications (alarm/call screens)
- Ongoing / persistent notifications
This fork fills the gap: it preserves all of Notifee's advanced features, migrates the bridge to React Native's New Architecture (TurboModules), and actively fixes the critical bugs left unresolved upstream. See the bug fix table below. It also supports Expo CNG / prebuild development builds through an official config plugin. Expo Go is not supported.
Project Status
- Officially recommended by Invertase as the community-maintained fork (April 2026)
- Maintained fork of Notifee — actively developed and published as
@felipe-software/react-native-notify-kit - New Architecture only (TurboModules)
- Expo CNG / prebuild support for development builds. Expo Go is not supported.
- iOS FCM Mode Notification Service Extension automation via config plugin.
- Android foreground service manifest configuration via config plugin, with explicit opt-in for foreground service types.
- Android Expo FCM smoke validation using RNFirebase data-only messages and
notifee.handleFcmMessage. - Minimum supported React Native:
0.73 - Development target: React Native
0.84 - License:
Apache-2.0 - Full changelog: CHANGELOG.md
The native core (NotifeeCore) is compiled from source as part of the bridge module (since 9.2.0) and the public API is 100% compatible with the original @notifee/react-native — migration is a safe, drop-in replacement.
Installation
yarn add @felipe-software/react-native-notify-kit
# or
npm install @felipe-software/react-native-notify-kitFor iOS, run cd ios && pod install after installing.
Migration from @notifee/react-native
If you're coming from the original Notifee package, migrating takes just a few steps:
Swap the package:
yarn remove @notifee/react-native yarn add @felipe-software/react-native-notify-kitUpdate imports — find and replace across your codebase:
- import notifee from '@notifee/react-native'; + import notifee from '@felipe-software/react-native-notify-kit';The default export is still called
notifee, so your application code stays the same — only the import path changes.Reinstall pods (iOS):
cd ios && pod install
No native code changes are required. The public API is fully compatible with @notifee/react-native.
Quick Start
import notifee, { AndroidImportance } from '@felipe-software/react-native-notify-kit';
// 1. Request permission (required on Android 13+ and iOS)
await notifee.requestPermission();
// 2. Create a channel (Android only, required for Android 8+)
await notifee.createChannel({
id: 'default',
name: 'Default Channel',
importance: AndroidImportance.HIGH,
});
// 3. Display a notification
await notifee.displayNotification({
title: 'Hello',
body: 'This is a local notification',
android: { channelId: 'default' },
});Note: The default export name
notifeeis kept intentionally for backward compatibility. If you're migrating from@notifee/react-native, a simple find-and-replace of the import path is all you need.
4. Handle events
In your index.js (before AppRegistry.registerComponent):
import notifee from '@felipe-software/react-native-notify-kit';
// Background/killed state events
notifee.onBackgroundEvent(async ({ type, detail }) => {
console.log('Background event:', type, detail.notification?.id);
});In your React component:
import { useEffect } from 'react';
import notifee, { EventType } from '@felipe-software/react-native-notify-kit';
useEffect(() => {
return notifee.onForegroundEvent(({ type, detail }) => {
if (type === EventType.PRESS) {
console.log('Notification pressed:', detail.notification?.id);
}
});
}, []);Which handler fires when (iOS):
- Tap a notification while the app is active in foreground →
onForegroundEventreceivesPRESS.- Tap a notification while the app is in background or killed →
onBackgroundEventreceivesPRESS, even though iOS immediately brings the app to the foreground right after. At the moment iOS delivers the tap to the delegate,UIApplication.applicationStateisInactive, notActive, so the event is routed to the background handler.- Foreground delivery of a Notifee-owned notification →
onForegroundEventreceivesDELIVERED.Register both handlers if you need to react to taps in every app state. Resolves the confusion reported in upstream invertase/notifee#1155.
Notifee FCM Mode (NEW in 10.0.0, Expo CNG in 10.4.0)
Use @felipe-software/react-native-notify-kit as the sole FCM display layer on both Android and iOS: one developer API, no duplicate notifications on Android, no silent-push drops on iOS. Ship a server SDK payload, let the client handle it in one line, and set up the iOS Notification Service Extension with the Expo config plugin or, for bare React Native, the init-nse CLI. On Android, FCM Mode remains data-only: RNFirebase receives the message and calls notifee.handleFcmMessage.
# server: build the payload
import { buildNotifyKitPayload } from '@felipe-software/react-native-notify-kit/server';
await admin.messaging().send(buildNotifyKitPayload({ token, notification: { title, body, android, ios } }));
# client (Android + iOS): one line in setBackgroundMessageHandler / onMessage
await notifee.handleFcmMessage(remoteMessage);
# iOS NSE setup for bare React Native
npx react-native-notify-kit init-nse && cd ios && pod installExpo CNG / development builds
Expo CNG / development builds are supported. Expo Go is not supported because this library requires native modules and native notification targets/capabilities.
On iOS, add the config plugin to your Expo config and run prebuild; the plugin generates and wires the Notification Service Extension used by NotifyKit FCM Mode.
export default {
expo: {
name: 'MyApp',
slug: 'my-app',
ios: {
bundleIdentifier: 'com.example.myapp',
},
plugins: [
[
'@felipe-software/react-native-notify-kit',
{
ios: {
notificationServiceExtension: true,
},
},
],
],
},
};On Android, FCM Mode stays data-only. Configure Firebase and RNFirebase in the app, create the Android channel used by your payloads, then call notifee.handleFcmMessage(remoteMessage) from RNFirebase onMessage and setBackgroundMessageHandler. The Expo smoke app validates foreground and background delivery with the android-expo-smoke scenario on a real device.
Android foreground service manifest configuration is also available through the NotifyKit config plugin, but it is explicit opt-in:
export default {
expo: {
plugins: [
[
'@felipe-software/react-native-notify-kit',
{
android: {
foregroundService: {
types: ['shortService'],
},
},
},
],
],
},
};The plugin does not configure Firebase, does not add USE_EXACT_ALARM, does not add USE_FULL_SCREEN_INTENT, and does not choose a foreground service type by default.
See the full guide: docs/fcm-mode.mdx — covers architecture, server SDK reference, client API, NSE setup, payload schema, migration, troubleshooting, and known limitations.
Push Notifications (Firebase)
This library handles notification display and management. For receiving push notifications, pair it with @react-native-firebase/messaging:
Firebase setup remains the consumer app's responsibility. The NotifyKit Expo config plugin does not install or configure Firebase, does not copy google-services.json, and does not patch Gradle for Firebase.
New in 10.0.0: for a turnkey FCM integration that handles both platforms (no duplicate on Android, APNs-reliable on iOS), use Notifee FCM Mode instead of the manual pattern below. The sections that follow still apply for basic Firebase setup (google-services plugin, permissions, APNs capability).
Android setup
Add Firebase dependencies to your app:
yarn add @react-native-firebase/app @react-native-firebase/messagingAdd the google-services plugin to
android/build.gradle:classpath("com.google.gms:google-services:4.4.2")Apply the plugin in
android/app/build.gradle:apply plugin: "com.google.gms.google-services"Download
google-services.jsonfrom Firebase Console and place it inandroid/app/.Add
POST_NOTIFICATIONSpermission toAndroidManifest.xml(required for Android 13+):<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
iOS setup
Download
GoogleService-Info.plistfrom Firebase Console and add it to your Xcode project.Enable Push Notifications capability in Xcode:
- Select your target > Signing & Capabilities > + Capability > Push Notifications
Enable Background Modes > Remote notifications:
- Select your target > Signing & Capabilities > + Capability > Background Modes > check Remote notifications
Configure APNs certificates or keys in Firebase Console > Project Settings > Cloud Messaging.
Display a push notification
import messaging from '@react-native-firebase/messaging';
import notifee from '@felipe-software/react-native-notify-kit';
messaging().onMessage(async remoteMessage => {
await notifee.displayNotification({
title: remoteMessage.notification?.title,
body: remoteMessage.notification?.body,
android: { channelId: 'default' },
});
});iOS Notification Service Extension
To modify push notification content before display (e.g., attach images), create a Notification Service Extension. FCM Mode supports two automated setup paths: Expo CNG uses the config plugin, while bare React Native uses the init-nse CLI.
Expo CNG automated setup
Use this for Expo prebuild / development builds:
export default {
expo: {
name: 'MyApp',
slug: 'my-app',
ios: {
bundleIdentifier: 'com.example.myapp',
},
plugins: [
[
'@felipe-software/react-native-notify-kit',
{
ios: {
notificationServiceExtension: true,
},
},
],
],
},
};Run Expo prebuild or an EAS development build after adding the plugin. The plugin adds the EAS appExtensions entry and generates the Swift service, Info.plist, entitlements, Xcode target, .appex embed phase, and Podfile target. Expo Go is not supported.
Bare React Native automated setup
From your project root, with @felipe-software/react-native-notify-kit installed:
npx react-native-notify-kit init-nse
cd ios && pod installThe CLI scaffolds a Swift NSE target (default name: NotifyKitNSE), patches your Podfile, and wires .pbxproj. Open your .xcworkspace in Xcode, verify the NSE target's signing, and build. Expo users should use the config plugin instead of running init-nse against generated native folders. See docs/fcm-mode.mdx#ios-nse-setup for the full CLI reference (target name, bundle suffix, --dry-run, --force).
Manual setup
For bare React Native projects with non-standard iOS paths or heavily-customized Xcode configurations where automation cannot patch the project cleanly:
In Xcode: File > New > Target > Notification Service Extension
Add to your Podfile:
target 'YourNSETarget' do pod 'RNNotifeeCore', :path => '../node_modules/@felipe-software/react-native-notify-kit' endUse
NotifeeExtensionHelperin yourNotificationService.m:#import "NotifeeExtensionHelper.h" - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent *))contentHandler { self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; [NotifeeExtensionHelper populateNotificationContent:request withContent:self.bestAttemptContent withContentHandler:contentHandler]; }Implement
serviceExtensionTimeWillExpireas a safety net. Notification Service Extensions have a ~30-second time budget; if your notification includes a large image attachment and the download is slow, the extension may be terminated before the content handler is called. Deliver a best-effort notification in the expiration handler:- (void)serviceExtensionTimeWillExpire { // Deliver the notification with whatever content we have so far // (e.g., without the image attachment if the download didn't finish). self.contentHandler(self.bestAttemptContent); }Run
cd ios && pod install
Server SDK
@felipe-software/react-native-notify-kit ships a zero-dependency server SDK under the /server subpath, for building FCM HTTP v1 payloads that the client handleFcmMessage handler consumes. Runs in Node.js 22+ and Firebase Cloud Functions.
import { buildNotifyKitPayload } from '@felipe-software/react-native-notify-kit/server';
import * as admin from 'firebase-admin';
const message = buildNotifyKitPayload({
token: '<device FCM token>',
notification: {
id: 'order-42',
title: 'Your order is on the way',
body: 'Tap to see live tracking',
data: { orderId: '42' },
android: { channelId: 'orders', smallIcon: 'ic_notification' },
ios: { sound: 'default', interruptionLevel: 'timeSensitive' },
},
options: { androidPriority: 'high', ttl: 3600 },
});
await admin.messaging().send(message);Full reference: docs/fcm-mode.mdx#server-sdk-reference (types, validation rules, payload shape, FCM 4 KB limit).
CLI Tools
The library ships a small CLI at npx react-native-notify-kit. Currently one command is available:
npx react-native-notify-kit init-nse— scaffolds an iOS Notification Service Extension (Swift), patches the Podfile, and wires.pbxproj. See docs/fcm-mode.mdx#ios-nse-setup for options.
Expo CNG users should use the config plugin for NSE setup instead of the CLI.
The CLI is prepacked into the main package at publish time, so npx react-native-notify-kit works immediately after yarn add @felipe-software/react-native-notify-kit — no separate install.
Jest Testing
Mock the native module in your Jest setup file:
// jest.setup.js
jest.mock('@felipe-software/react-native-notify-kit', () =>
require('@felipe-software/react-native-notify-kit/jest-mock'),
);Add to your Jest config:
setupFiles: ['<rootDir>/jest.setup.js'],
transformIgnorePatterns: [
'node_modules/(?!(jest-)?react-native|@react-native|@felipe-software/react-native-notify-kit)'
],What's Different from Notifee
This fork is a complete migration to React Native's New Architecture:
- TurboModules only — no legacy Bridge support (
NativeModulesreplaced withTurboModuleRegistry) - Android bridge rewritten in Kotlin (original was Java)
- iOS bridge uses Objective-C++ with
NativeNotifeeModuleSpecJSITurboModule conformance - Minimum React Native 0.73, development target 0.84
- Toolchain: Yarn 4, Node 22+, Java 17, compileSdk 36 / targetSdk 35
- Single Android module — the original Notifee shipped a pre-compiled AAR bundled inside the npm tarball under a frozen Maven coordinate; this fork compiles the core from source as part of the React Native bridge module on every consumer build. Eliminates the
FAIL_ON_PROJECT_REPOSissue on RN 0.74+ and the Gradle cache staleness bug that could serve outdated bytecode afteryarn upgrade. - Expo CNG development builds — iOS FCM Mode NSE automation and Android foreground service manifest configuration are available through the config plugin. Android Expo FCM smoke validation uses RNFirebase data-only messages plus
notifee.handleFcmMessage. - Core notification logic (NotifeeCore) is unchanged — the public API is fully compatible with the original Notifee
- 35 upstream bugs fixed — see Bugs Fixed from Upstream Notifee below
- Reliable trigger notifications — AlarmManager is the default backend instead of WorkManager, with automatic fallback when exact alarm permission is not granted
- Custom repeat intervals for timestamp triggers —
TimestampTrigger.repeatIntervalsupports calendar-based recurrences such as every 2 days, every 2 weeks, or every 3 months from a selected start timestamp. On iOS, repeating timestamp triggers now use a bounded rolling schedule of one-shot local notifications instead of native repeating calendar triggers; this enables custom repeat intervals and start-date-respecting recurrence, but apps that relied on native iOS repeating triggers being scheduled indefinitely should review the iOS notes in the Triggers guide. - New API:
setNotificationConfig()— opt-out flag to prevent Notifee from intercepting iOS remote notification handlers (see New APIs below) - Baseline Profile — the library AAR ships a Baseline Profile that instructs ART to AOT-compile the foreground service notification hot path at install time, eliminating JIT penalty on first invocation
Bugs Fixed from Upstream Notifee
This fork fixes the following bugs that were never resolved in the original Notifee repository:
| Bug | Platform | Upstream Issue | Fixed in |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Notifee intercepts iOS remote notification tap handlers, breaking RNFB onNotificationOpenedApp / getInitialNotification | iOS | #912 | 9.1.12 |
| completionHandler not called on notification dismiss | iOS | Pre-existing | 9.1.12 |
| completionHandler not called in willPresentNotification fallback | iOS | Pre-existing | 9.1.12 |
| getInitialNotification() returns null on cold start (deprecated UIApplicationLaunchOptionsLocalNotificationKey check) | iOS | #1128 | 9.1.12 |
| willPresentNotification: fallback silently drops foreground notifications when no original delegate is captured (returns None instead of platform defaults) | iOS | Pre-existing (introduced by partial fix in v9.1.12) | 9.1.20 |
| All delivered notifications dismissed from Notification Center when the app is opened | iOS | #828 | 9.1.20 |
| getInitialNotification() returns null without pressAction configured | Android | #1128 | 9.1.12 |
| Foreground press events silently dropped when React instance not ready | Android | #1279 | 9.1.12 |
| Trigger notifications not firing on Android 14-15 when app is killed (missing goAsync() in BroadcastReceiver) | Android | #1100 | 9.1.12 |
| SCHEDULE_EXACT_ALARM denial silently drops scheduled alarms (no fallback) | Android | #1100 | 9.1.12 |
| getNotificationSettings() returns DENIED instead of NOT_DETERMINED on Android 13+ before permission requested | Android | #1237 | 9.1.12 |
| Default AlarmType.SET_EXACT doesn't work in Doze mode; AlarmType.SET uses RTC instead of RTC_WAKEUP | Android | #961 | 9.1.12 |
| Foreground service crashes with ANR after ~3 min on Android 14+ (shortService timeout, missing onTimeout()) | Android | #703, #1107 | 9.1.13 |
| Manifest merger failure when overriding foregroundServiceType on ForegroundService | Android | #1108 | 9.1.13 |
| Foreground service notifications dismissible on Android 13+ even with ongoing: true (library doesn't auto-set ongoing for foreground services) | Android | #1248 | 9.1.14 |
| DST (daylight saving time) shifts repeating scheduled notifications by ±1 hour | Android | #875 | 9.1.14 |
| != reference equality on String comparison in NotificationPendingIntent (latent — would activate when getLaunchActivity() returns a non-null value for id=default) | Android | Pre-existing (latent) | 9.1.19 |
| pressAction.launchActivity not defaulted at native layer when pressAction.id === 'default' | Android | N/A (defense-in-depth) | 9.1.19 |
| Duplicate symbols linker error when using NSE ($NotifeeExtension = true) with static frameworks — NotifeeExtensionHelper compiled by both RNNotifee and RNNotifeeCore pods | iOS | Pre-existing | 9.1.22 |
| Could not resolve app.notifee:core:+ / FAIL_ON_PROJECT_REPOS rejection — library injected a Maven repository into the consumer's rootProject.allprojects block, which broke on (a) RN 0.74+ with dependencyResolutionManagement, (b) Expo SDK 53/54 where extraMavenRepos is not propagated to subprojects, and (c) Gradle 8 dependency locking with legacy XML parsers | Android | #1079, #1226, #1262 | 9.2.0 |
| Stale Gradle cache could serve outdated AAR bytecode after yarn upgrade — same Maven coordinate reused across releases violated Gradle's coordinate-immutability assumption | Android | N/A (architectural) | 9.2.0 |
| EventType.DELIVERED not emitted for displayNotification() in foreground (only for trigger notifications) — notifeeTrigger != nil guard in willPresentNotification: suppressed the event, breaking iOS/Android symmetry | iOS | Pre-existing | 9.3.0 |
| Tapping a notification without explicit pressAction does nothing (app doesn't open) — NotificationPendingIntent.createIntent() creates a tap-less PendingIntent when pressActionModelBundle is null, especially visible on trigger notifications after app kill | Android | Pre-existing (latent), #291 | 9.3.0 |
| Foreground service notifications delayed up to 10 seconds on Android 12+ — library never calls setForegroundServiceBehavior(FOREGROUND_SERVICE_IMMEDIATE) | Android | #272, #1242 | 9.4.0 |
| didReceiveNotificationResponse: completionHandler delayed by 15 seconds via dispatch_after, blocking subsequent notification taps and risking handler leaks if the app is suspended during the wait | iOS | Pre-existing (TODO since 2020) | 9.4.0 |
| requestPermission: silently swallows NSError from requestAuthorizationWithOptions, making MDM and parental-control authorization failures invisible to JS consumers | iOS | Pre-existing (TODO since day 1) | 9.4.0 |
| contentByUpdatingWithProvider: errors suppressed via nil error pointer in displayNotification: and createTriggerNotification: — communication notifications with malformed SiriKit intents silently fail with nil content | iOS | Pre-existing | 9.4.0 |
| getBadgeCount: completion block never called when running in an app extension, causing JS promises to hang forever in NSE handlers | iOS | Pre-existing | 9.4.0 |
| Notification Service Extension attachment downloads had no timeout cap (default 60-second NSURLSession timeout exceeds iOS's ~30-second NSE budget), causing extension process kill and notification loss on slow networks | iOS | Pre-existing | 9.4.0 |
| cancelTriggerNotifications() / createTriggerNotification() promises resolve before Room DB write completes, causing ~3% race on cancel-then-create patterns. Also fixes a previously-undocumented reboot-recovery data-loss bug in NotifeeAlarmManager.rescheduleNotification and an ordering bug in NotificationManager.doScheduledWork | Android | #549 | 9.5.0 |
| Scheduled trigger notifications silently lost across device reboot on OEM devices (Xiaomi MIUI, OnePlus, Huawei EMUI, Oppo ColorOS, Vivo FuntouchOS) whose vendor OS suppresses BOOT_COMPLETED until the user manually enables autostart. Also handles zombie non-repeating triggers whose fire time already passed (fire-once within a 24-hour grace period, then delete the Room row; delete silently beyond the grace period) and adds try/catch/finally guards to all notifee BroadcastReceiver async paths. | Android | #734 | 9.6.0 |
| RepeatFrequency.DAILY / WEEKLY triggers fire on day 1 but never on day 2+ (also reproduces with arrays of 24 daily reminders) — pre-fix: stale Room anchors after the post-fire repeat recalculation, plus DST ±1h shift on repeating triggers, plus reboot recovery silently dropping the rearmed PendingIntent on OEM devices that suppress BOOT_COMPLETED | Android | #601, #1063 | 9.1.14 (DST + persist recalc) + 9.5.0 (await Room) + 9.6.0 (BOOT_COUNT cold-start, race guard) |
| Scheduled TIMESTAMP trigger notifications lost after device reboot on Android 14 emulator with battery optimizations off — pre-fix: RebootBroadcastReceiver dropped the goAsync handoff before Room writes completed; OEM devices that suppress BOOT_COMPLETED never re-armed the alarm at all | Android | #991 | 9.1.12 (goAsync in RebootBroadcastReceiver) + 9.5.0 (await Room in rescheduleNotification) + 9.6.0 (BOOT_COUNT cold-start for OEM suppressors) |
| ObjectAlreadyConsumedException in headless task when the same WritableMap is reused or the taskConfig accessor is read twice — TaskConfig.init mutated the caller's map instead of copying it first. Latent in most apps but observed in production by upstream users with high-frequency headless events | Android | #266 | 9.6.0 |
| getDisplayedNotifications() returned no data field on Android, breaking iOS/Android API symmetry where iOS exposes custom keys via parseDataFromUserInfo: (see platform limitation note below — the fix is API parity for app-posted notifications, not a workaround for FCM background auto-display) | Android | #393 | 9.7.0 |
| Small icon resolution failure in release builds causes IllegalArgumentException at NotificationCompat.Builder.build() — library now falls back to the app launcher icon and logs a warning instead of failing the notification display | Android | #733 | 10.1.0 |
Important note on
getDisplayedNotifications()and FCM custom data on Android.The 9.7.0 fix for #393 makes
getDisplayedNotifications()expose adatafield for notifications stored inNotification.extras(matching iOS shape). However, when the FCM Android SDK auto-displays anotification+datapush while the app is in background or killed, customdatafields are placed only on the tap-actionPendingIntent— never onNotification.extras. This is the FCM SDK's original design (seeCommonNotificationBuilder.createContentIntentin firebase-android-sdk) and is not workaroundable from any library — the PendingIntent's extras are sealed by Android's security model. Firebase tracks this gap in firebase-android-sdk#2639 (open since 2021).The fix does work for these scenarios on Android: notifications created via
notifee.displayNotification({ data: {...} }), FCM data-only messages handled inonMessageReceivedfollowed by an explicitdisplayNotification()call, customFirebaseMessagingService.handleIntentoverrides that inject extras before display, and notifications from other libraries that callNotificationCompat.Builder.addExtras(bundle).Recommended pattern for full control over FCM push notifications on Android: send FCM data-only messages (omit the
notificationfield server-side), handle them inonMessageReceived(or a headless task in killed state), and render the notification yourself vianotifee.displayNotification(). This gives full control overdata, channel, styling, tap behavior, and is also Firebase's official recommendation.Reserved keys filtered from
data(custom payload keys matching these are dropped): prefixesandroid.,google.,gcm.,fcm.(with trailing dot —fcmRegion,googleishsurvive),notifee(no trailing dot — library's reserved namespace,notifeeFoois also filtered), plus exact keysfrom,collapse_key,message_type,message_id,aps,fcm_options. Thefcm_optionsexact-match matches iOS behavior on the Firebase analytics-label key. Cross-platform note: bare-fcmkeys other thanfcm_options(e.g.fcmRegion,fcmToken) are preserved on Android but filtered on iOS — rename server-side if you need strict parity.
Note for apps requiring guaranteed exact alarms (alarm clocks, timers, calendars): Add
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />to your app'sAndroidManifest.xml. This permission is auto-granted and not revocable, but Google Play restricts its use to apps whose core function requires exact timing. For all other apps, the library usesSCHEDULE_EXACT_ALARMwith automatic fallback to inexact alarms when the permission is not granted.
As bugs are fixed, this table is updated. See CHANGELOG.md for full details.
Documented Workarounds for Platform Limitations
Some upstream Notifee issues are not bugs in the library itself but platform-level limitations imposed by Android's Doze mode and vendor power management — no library code can make AlarmManager deliver an alarm to, or a foreground service survive inside, an app the OEM has explicitly paused. For these, the fork provides documented mitigations: user-facing helper APIs, code-level self-healing where possible, and decision guides that steer consumers toward the Android primitive most resilient to the specific vendor policy.
| Upstream issue | Symptom | Platform root cause | Fork mitigation |
| ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| invertase/notifee#410 | Foreground service paused on screen lock (Samsung OneUI, ~6 seconds after screen off on battery) and killed immediately when the app is backgrounded (Xiaomi MIUI) | Vendor aggressive battery-saver and autostart policies suspend or terminate foreground services of apps not whitelisted in the OEM's protected-apps / autostart settings. Partially Doze-related on non-exempt foregroundServiceType values; mostly OEM-specific behavior catalogued at dontkillmyapp.com. | (1) Decision guide — the Timers: foreground service or SET_ALARM_CLOCK? section recommends the SET_ALARM_CLOCK trigger over a silent foreground service for rest, cooking, and recovery timer use cases. setAlarmClock is the same primitive the stock Clock app uses and is generally respected by vendor aggressive-kill policies. (2) Foreground service use case matrix — the Foreground service use case guide documents which foregroundServiceType values are Doze-CPU-exempt, which have type-specific timeouts, and the Google Play policy constraints that rule out misusing mediaPlayback for silent timers. (3) openPowerManagerSettings() helper API — points users toward known vendor autostart / protected-apps screen candidates when available; these links are best-effort because firmware variants may move, block, or remove those settings activities. |
| invertase/notifee#734 | Scheduled trigger notifications silently lost across a device reboot on OEM devices (Xiaomi MIUI, OnePlus, Huawei EMUI, Oppo ColorOS, Vivo FuntouchOS) | The vendor OS suppresses the BOOT_COMPLETED broadcast to apps the user has not manually whitelisted, so the library's RebootBroadcastReceiver never runs and persisted AlarmManager triggers are never re-armed after reboot. | (1) BOOT_COUNT cold-start self-heal (code) — on every app init, InitProvider compares Settings.Global.BOOT_COUNT against the last-known value in SharedPreferences and re-arms every persisted trigger on a background thread if a boot delta is detected, even when BOOT_COMPLETED was never delivered. Paired with a process-wide AtomicBoolean race guard in NotifeeAlarmManager.rescheduleNotifications that prevents double-advancement when the reboot receiver and the cold-start path race. (2) openPowerManagerSettings() helper API — the same vendor-settings deep-link used by #410, pointing the user at the autostart whitelist for defense in depth. |
| invertase/notifee#927 | Custom sound passed via displayNotification({ android: { sound, channelId }, ios: { sound } }) is ignored for remote push notifications (FCM/APNs) delivered while the app is in background or killed — the system default sound plays instead. Foreground delivery and locally-scheduled notifications (displayNotification, createTriggerNotification) are unaffected. | When a remote push arrives while the app is killed, the JavaScript layer never runs — the system tray item is drawn by the OS (Android system + Firebase SDK; iOS + APNs) before any Notifee code executes. On Android API 26+, the NotificationChannel sound is set once at channel creation and is immutable thereafter — NotificationCompat.Builder.setSound() is silently ignored when the builder has a channelId. On
