In the first part of this series, we built a complete Firebase Cloud Messaging (FCM) setup for React Native using Firebase and Notifee.
Now, in Part 2, we’ll go deeper and explore:
- 📱 Local notifications
- ✨ Custom notification styles
- ⏰ Triggers and scheduling
- ⚡ Action buttons and backend integration
- 🎧 Media and chat-style notifications
- 📊 Event tracking for every app state (Foreground, Background, Killed)
This part focuses on building a robust notification system that behaves consistently across app states — with fixes to ensure reliable delivery and accurate navigation.
📱 1. Local Notifications with Notifee
Local notifications are shown without FCM, usually triggered by in-app events — such as reminders or task completions.
import notifee, { AndroidImportance, TriggerType } from '@notifee/react-native';
// Simple local notification
async function showLocalNotification() {
await notifee.requestPermission();
const channelId = await notifee.createChannel({
id: 'local',
name: 'Local Notifications',
importance: AndroidImportance.HIGH,
});
await notifee.displayNotification({
title: "'NotifyMeApp Reminder',"
body: 'Your scheduled task is ready!',
android: {
channelId,
smallIcon: 'ic_launcher',
},
});
}
You can call showLocalNotification() anywhere — even as a response to user actions or events.
⏰ 2. Notification Triggers
Notifee allows scheduled or event-based notifications.
Example: Trigger after 10 seconds
const trigger = {
type: TriggerType.TIMESTAMP,
timestamp: Date.now() + 10 * 1000, // 10 seconds later
};
await notifee.createTriggerNotification(
{
title: 'NotifyMeApp',
body: '⏰ Scheduled notification fired!',
android: { channelId: 'local' },
},
trigger
);
Example: Repeat daily reminder
const trigger = {
type: TriggerType.INTERVAL,
interval: 24 * 60 * 60, // every 24 hours
};
await notifee.createTriggerNotification(
{
title: 'Daily Reminder',
body: 'It’s time to check new updates!',
android: { channelId: 'local' },
},
trigger
);
🧱 3. Customizing Notifications
You can completely customize your notification look using Notifee’s Android styles.
Big Picture Style (with Image)
await notifee.displayNotification({
title: 'New Offer Available 🎉',
body: 'Get 20% off on your next rental!',
android: {
channelId: 'promo',
style: {
type: notifee.AndroidStyle.BIGPICTURE,
picture: 'https://example.com/offer-banner.jpg',
},
},
});
Messaging Style (for Chat)
await notifee.displayNotification({
title: 'John Doe',
body: 'Hey! Are you available for the shoot tomorrow?',
android: {
channelId: 'chat',
style: {
type: notifee.AndroidStyle.MESSAGING,
person: { name: 'John Doe' },
messages: [
{ text: 'Hey! Are you available for the shoot tomorrow?', timestamp: Date.now() },
],
},
},
});
⚙️ 4. Notification Actions (with Backend Support)
Add buttons directly to your notifications to trigger specific actions.
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="OPEN_ACTIVITY_1" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Then in your notification payload (from backend):
const message = {
token,
notification: {
title: 'NotifyMeApp',
body: 'New message received',
},
android: {
channelId: 'chat',
actions: [
{
title: 'Reply',
pressAction: {
id: 'OPEN_ACTIVITY_1',
},
},
],
},
};
In your app:
notifee.onForegroundEvent(({ type, detail }) => {
if (type === notifee.EventType.ACTION_PRESS && detail.pressAction.id === 'OPEN_ACTIVITY_1') {
console.log('User tapped Reply button');
// Backend can be notified here (e.g., mark as read or open chat)
}
});
🧠 5. Notification Events Across App States
Recent improvements have made notifications behave consistently across all app states.
Here’s how you can handle each transition cleanly 👇
| Received In | Opened From | Expected Behavior |
|---|---|---|
| Foreground | Foreground | Show custom in-app popup or Notifee local notification |
| Foreground | Background | Navigate to target screen after resume |
| Foreground | Killed | Use getInitialNotification() to handle navigation |
| Background | Foreground | Auto-navigate to notification target |
| Background | Background | Open directly to target screen |
| Background | Killed | Retrieve data from FCM launch event |
| Killed | Foreground | Fetch from last FCM message using onNotificationOpenedApp
|
| Killed | Background | Same as above — delayed navigation allowed |
| Killed | Killed | Delivered silently; handle upon next app launch |
Implementation Example
import messaging from '@react-native-firebase/messaging';
import notifee from '@notifee/react-native';
import { navigationRef } from './NavigationService';
// Foreground messages
messaging().onMessage(async (remoteMessage) => {
await notifee.displayNotification({
title: remoteMessage.notification?.title,
body: remoteMessage.notification?.body,
android: { channelId: 'default' },
});
});
// When app is opened from background
messaging().onNotificationOpenedApp((remoteMessage) => {
const screen = remoteMessage.data?.targetScreen;
if (screen) navigationRef.current?.navigate(screen);
});
// When app is launched from a killed state
messaging()
.getInitialNotification()
.then((remoteMessage) => {
if (remoteMessage) {
const screen = remoteMessage.data?.targetScreen;
if (screen) navigationRef.current?.navigate(screen);
}
});
🎧 6. Media & Chat Notifications
To include images, videos, or chat previews inside notifications:
await notifee.displayNotification({
title: '🎬 Video Upload Complete!',
body: 'Your project is now ready to share.',
android: {
channelId: 'media',
style: {
type: notifee.AndroidStyle.BIGPICTURE,
picture: 'https://example.com/video-thumbnail.jpg',
},
},
});
For chat-like experience:
await notifee.displayNotification({
title: 'Anna',
body: 'Hey! New project details are ready!',
android: {
channelId: 'chat',
style: {
type: notifee.AndroidStyle.MESSAGING,
person: { name: 'Anna' },
messages: [
{ text: 'Hey! New project details are ready!', timestamp: Date.now() },
{ text: 'Let’s discuss soon.', timestamp: Date.now() },
],
},
},
});
✅ Key Fixes and Improvements
In the latest implementation:
- Notifications tapped from a killed state now navigate correctly.
- Foreground notifications no longer duplicate when both FCM and Notifee handlers are active.
- Deep links from background notifications navigate smoothly on resume.
Resolved by:
- Using
getInitialNotification()for cold starts - Ensuring single Notifee channel creation
- Separating local + FCM handlers
- Maintaining event order for consistent navigation
🎯 Wrapping Up
In this advanced guide, we covered:
- Local & scheduled notifications
- Trigger-based alerts
- Media and chat-style notifications
- Custom action buttons integrated with backend
- Complete event handling across all app states
Together with Part 1, you now have a production-ready notification system for any React Native app.
✍️ Written by Dainy Jose — Mobile App Developer specialized in React Native and the MERN stack.
💼 Skills & Tools:
Mobile App Dev | MERN Stack | React Native | TypeScript | Redux | React.js | Node.js | MongoDB | MySQL | Express.js | REST API | JWT | Google Maps | Firebase | Jest | Agile | SDLC | Payments | Git | Bitbucket | Jira
📬 Connect with me:
