<< All versions

Skill v1.0.1

currentAutomated scan100/100
khadinakbarlabs/expo-mobile-app-builder/add-expo-notifications-fcm

+3 new

──Details
PublishedOctober 23, 2026 at 06:22 PM
Content Hashsha256:ea5880be5a0dc7f0...
Git SHA3b1834833207
Bump Typepatch
Compare with v1.0.0
──Files
Files (1 file, 3.4 KB)
SKILL.md3.4 KBactive
SKILL.md · 125 lines · 3.4 KB

version: "1.0.1" name: "add-expo-notifications-fcm" description: "Set up Firebase Cloud Messaging (FCM) push notifications via expo-notifications for Android. Use when the user says 'add fcm', 'push notifications android', 'expo notifications android', 'firebase push'."


Add FCM Push (expo-notifications)

Firebase Cloud Messaging — free, unlimited push for Android.

Install

bash
npm exec --no -- expo install expo-notifications expo-device

Firebase project setup

Run set-up-firebase-cli skill first. Result: google-services.json in android/app/.

app.json:

json
{
"expo": {
"android": { "googleServicesFile": "./google-services.json" },
"notification": {
"icon": "./assets/notification-icon.png",
"color": "#ffffff",
"androidMode": "default"
}
}
}

Request permission (Android 13+ runtime)

tsx
import * as Notifications from 'expo-notifications';
import * as Device from 'expo-device';
async function registerForPush() {
if (!Device.isDevice) return;
let { status } = await Notifications.getPermissionsAsync();
if (status !== 'granted') {
const r = await Notifications.requestPermissionsAsync();
if (r.status !== 'granted') return null;
}
const token = (await Notifications.getDevicePushTokenAsync()).data;
// FCM token — save to backend
return token;
}

Custom pre-prompt (boost permission opt-in)

Before calling requestPermissionsAsync, show your own screen:

"Get a daily reminder. We promise no spam."
[Sure] [Later]

If "Sure" → trigger system dialog. If "Later" → don't ask again this session.

Notification channels (Android 8+)

tsx
import * as Notifications from 'expo-notifications';
Notifications.setNotificationChannelAsync('daily-reminders', {
name: 'Daily Reminders',
importance: Notifications.AndroidImportance.HIGH,
sound: 'default',
vibrationPattern: [0, 250, 250, 250],
lightColor: '#FF231F7C',
});

Channels are user-controllable in Settings. Group related notifs.

Foreground handler

tsx
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: true,
}),
});

Send from backend

Use Firebase Admin SDK or Expo Push API:

ts
fetch('https://exp.host/--/api/v2/push/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
to: deviceToken,
title: 'Reminder',
body: 'Time to log today',
channelId: 'daily-reminders',
}),
});

Deep link on tap

tsx
const notificationRoutes = new Set(['/today', '/library']);
Notifications.addNotificationResponseReceivedListener((response) => {
const screen = response.notification.request.content.data.screen;
if (typeof screen === 'string' && notificationRoutes.has(screen)) {
router.push(screen);
}
});

Manufacturer battery optimization (CRITICAL)

Xiaomi (MIUI), Oppo (ColorOS), OnePlus, Samsung kill background apps aggressively. Push works most of the time but for ENGAGEMENT push (reminders), users must:

  • Settings → Battery → [Your app] → "Don't optimize"
  • Some need: Settings → Apps → [Your app] → Battery saver → "No restriction"

Document this in onboarding for power users.

Pair with

  • add-onesignal-push for advanced campaigns (RC for transactional, OS for marketing)
  • add-deep-links to handle taps
← v1.0.0All versions