Push Notifications — Firebase FCM
Push notifications re-engage users when the app is closed. The flow is: app registers with FCM → receives a device token → your server sends a notification to that token via FCM → FCM delivers to the device. expo-notifications handles the React Native side.
Installation
npx expo install expo-notifications expo-device expo-constants @react-native-firebase/app @react-native-firebase/messaging
# Config plugins in app.config.ts:
# plugins: [['expo-notifications', { ... }], '@react-native-firebase/app', '@react-native-firebase/messaging']Full Push Notification Setup
import * as Notifications from 'expo-notifications';
import * as Device from 'expo-device';
import messaging from '@react-native-firebase/messaging';
import Constants from 'expo-constants';
// ---- How notifications behave when app is foregrounded ----
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: true,
}),
});
// ---- Register device and get push token ----
export async function registerForPushNotifications(): Promise<string | null> {
if (!Device.isDevice) {
console.warn('Push notifications only work on physical devices');
return null;
}
// 1. Request permission (iOS only — Android grants automatically)
const { status: existingStatus } = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync({
ios: { allowAlert: true, allowBadge: true, allowSound: true },
});
finalStatus = status;
}
if (finalStatus !== 'granted') return null;
// 2. Set Android notification channel (required for Android 8+)
await Notifications.setNotificationChannelAsync('default', {
name: 'Default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#6366f1',
sound: 'default',
showBadge: true,
});
// 3. Get FCM token (raw Firebase token — use for server-side send)
const fcmToken = await messaging().getToken();
// 4. Get Expo push token (use with Expo's send API)
const expoPushToken = await Notifications.getExpoPushTokenAsync({
projectId: Constants.expoConfig?.extra?.eas?.projectId,
});
console.log('FCM Token:', fcmToken);
console.log('Expo Push Token:', expoPushToken.data);
// 5. Send token to your backend
await apiClient.post('/users/me/push-token', { fcmToken, expoPushToken: expoPushToken.data });
return fcmToken;
}
// ---- Handle notifications in-app ----
export function usePushNotifications() {
const navigationRef = React.useRef<NavigationContainerRef<RootStackParamList>>(null);
useEffect(() => {
// Register on mount
registerForPushNotifications();
// App in foreground — notification received
const foregroundSub = Notifications.addNotificationReceivedListener(notification => {
console.log('Foreground notification:', notification);
// Show in-app banner or update badge
});
// User tapped notification (foreground or background)
const responseSub = Notifications.addNotificationResponseReceivedListener(response => {
const data = response.notification.request.content.data;
// Navigate to relevant screen
if (data.type === 'POST_LIKE' && data.postId) {
navigationRef.current?.navigate('PostDetail', { postId: data.postId as string });
}
});
// App was CLOSED — opened by notification tap (check initial notification)
Notifications.getLastNotificationResponseAsync().then(response => {
if (response?.notification.request.content.data.type === 'CHAT_MESSAGE') {
// Navigate to chat
}
});
// Background FCM (data-only messages — no UI, run JS)
messaging().setBackgroundMessageHandler(async remoteMessage => {
console.log('Background FCM:', remoteMessage);
// Update badge, download data, etc.
});
return () => {
foregroundSub.remove();
responseSub.remove();
};
}, []);
return { navigationRef };
}Tip
Tip
Practice Push Notifications Firebase FCM in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
React Native bridges JavaScript and native platform code
Practice Task
Note
Practice Task — (1) Write a working example of Push Notifications Firebase FCM from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with Push Notifications Firebase FCM is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready react native code.