React Native

Advanced Push & Local Notifications in React Native with Firebase and Notifee (Part 2)

October 28, 2025•5 min read•1 views
React Native

StackQuill

Advanced Push & Local Notifications in React Native with Firebase and Notifee (Part 2)

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.

js
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

js
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

js
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)

js
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)

js
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.

js
<!-- AndroidManifest.xml -->
<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):

js
const message = {
  token,
  notification: {
    title: 'NotifyMeApp',
    body: 'New message received',
  },
  android: {
    channelId: 'chat',
    actions: [
      {
        title: 'Reply',
        pressAction: {
          id: 'OPEN_ACTIVITY_1',
        },
      },
    ],
  },
};

In your app:

js
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 InOpened FromExpected Behavior
ForegroundForegroundShow custom in-app popup or Notifee local notification
ForegroundBackgroundNavigate to target screen after resume
ForegroundKilledUse getInitialNotification() to handle navigation
BackgroundForegroundAuto-navigate to notification target
BackgroundBackgroundOpen directly to target screen
BackgroundKilledRetrieve data from FCM launch event
KilledForegroundFetch from last FCM message using onNotificationOpenedApp
KilledBackgroundSame as above — delayed navigation allowed
KilledKilledDelivered silently; handle upon next app launch

Implementation Example

js
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:

js
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:

js
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.

Related Articles