Course Dashboard

Lesson 7: Foreground Behavior — Controlling the Experience

By default, most mobile operating systems suppress notification banners while the app is actively in the foreground. The theory is that if the user is already looking at your app, they don't need a distracting popup. However, there are many cases where you do want that banner to appear—think of a critical alert or a new message from a different chat room.

In this lesson, we will master:

  • The Notification Handler: The gatekeeper of foreground alerts.
  • Customizing Behavior: Dynamically deciding which notifications show banners, play sounds, or update badges.
  • In-App Receivers: Using listeners to update the UI instantly without banners.
  • UX Best Practices: Balancing engagement with user interruption.

The Notification Handler

In Expo, we use setNotificationHandler to define how the app should respond to notifications that arrive while the app is open. This is a global configuration that should be set at the start of your app's lifecycle.

index.tstypescript
import * as Notifications from 'expo-notifications';

Notifications.setNotificationHandler({
  handleNotification: async (notification) => {
    // Audit the incoming notification
    const { title } = notification.request.content;
    const isUrgent = title?.toLowerCase().includes('urgent');

    return {
      shouldShowAlert: isUrgent, // Only show a banner for urgent items
      shouldPlaySound: true,    // Always play the sound
      shouldSetBadge: false,   // Don't update the home screen icon badge
    };
  },
});

Updating the UI Internally

Even if you choose not to show a banner (shouldShowAlert: false), you still want your app to be aware of the new data. For this, we use the addNotificationReceivedListener.

This is ideal for "silent updates," such as showing a "New Message" indicator or refreshing a task list without interrupting the user's current flow.

index.tstypescript
useEffect(() => {
  const subscription = Notifications.addNotificationReceivedListener(notification => {
    const data = notification.request.content.data;
    console.log("Notification received in foreground:", data);
    
    // Update local state or trigger a re-fetch
    if (data.type === 'REFRESH_DATA') {
      refreshMyData();
    }
  });

  return () => subscription.remove();
}, []);

UX: The Art of Non-Interruption

Overusing foreground banners is a quick way to annoy users. Follow these professional guidelines:

  1. Context Awareness: If a user is already viewing "Project A," don't show a banner for a new comment on "Project A." Just update the view.
  2. Selective Banners: Only use shouldShowAlert: true for cross-context events (e.g., a message from a different user) or time-sensitive alerts.
  3. Silent Updates: Use listeners to keep the app fresh silently. The user will appreciate the "live" feel without the constant popups.

Implementation in Your Project

In a real-world scenario, you might want to switch behaviors based on the active screen:

index.tstypescript
Notifications.setNotificationHandler({
  handleNotification: async (notification) => {
    // Logic to check current route
    const currentRoute = getCurrentRoute(); // Custom logic
    const senderId = notification.request.content.data.senderId;

    if (currentRoute === 'Chat' && activeConversationId === senderId) {
      return { shouldShowAlert: false, shouldPlaySound: true };
    }

    return { shouldShowAlert: true, shouldPlaySound: true };
  },
});

Your Challenge

Update your setNotificationHandler to only show banners if the notification's body length is greater than 10 characters. Send two test notifications through your "Instant Test" button—one short and one long—and observe the difference in behavior.