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.tstypescriptimport * 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.tstypescriptuseEffect(() => { 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:
- 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.
- Selective Banners: Only use
shouldShowAlert: truefor cross-context events (e.g., a message from a different user) or time-sensitive alerts. - 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.tstypescriptNotifications.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.