agents-inc/skills/src/skills/mobile-notifications-push/SKILL.md
mobile-notifications-push
Push notification patterns - expo-notifications (Expo) and @react-native-firebase/messaging (bare RN), permission handling, token management, foreground/background/tap listeners, local scheduling, Android channels, notification categories and actions, badge management, rich notifications
- Source repository stars
- 23
- Declared platforms
- 0
- Static risk flags
- 0
- Last source update
- 2026-08-09
- Source checked
- 2026-08-28
Decision brief
What it does: where it fits
Quick Guide: Two main approaches: expo-notifications (Expo workflow, unified API for push + local) and @react-native-firebase/messaging (bare RN, FCM/APNs direct). Always request permissions before retrieving tokens. Handle three notification states: foreground (app open), backg…
Not for
- Tasks that require unconfirmed production actions or broad system permissions.
- Environments where the pinned source and install steps cannot be inspected.
Compatibility matrix
Platform support, with evidence labels
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
Inspect first. Install second.
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/agents-inc/skills --skill "src/skills/mobile-notifications-push"Inspect the Agent Skill "mobile-notifications-push" from https://github.com/agents-inc/skills/blob/81d43a51211aca12c85dcc16085fa99014ec548e/src/skills/mobile-notifications-push/SKILL.md at commit 81d43a51211aca12c85dcc16085fa99014ec548e. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
What the source asks the agent to do
- 01
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)
Sending remote push notifications to users via FCM/APNsRequesting and managing notification permissionsRetrieving and storing push tokens (Expo push token or native FCM/APNs token) - 02
Philosophy
Push notifications bridge the gap between your app and users when the app is not in focus. The two main approaches in React Native serve different workflows:
Permission first -- Always check and request permissions before any token or notification work. Requesting at a contextually appropriate moment (after user sees value) dramatically improves grant rates.Handle all three states -- Notifications arrive when the app is in foreground, background, or quit. Each state requires a different listener. Missing one means silently lost notifications.Channels are mandatory on Android -- Android 8+ (API 26+) requires notification channels. Without one, notifications are silently dropped. Create channels at app startup, not at send time. - 03
Core Patterns
Always check existing permission status before prompting. On iOS, the permission dialog can only be shown ONCE natively -- if denied, you must direct users to Settings.
Always check existing permission status before prompting. On iOS, the permission dialog can only be shown ONCE natively -- if denied, you must direct users to Settings.Why good: checks existing status first to avoid redundant prompts, handles denial gracefully, works on both platformsSee examples/core.md for the complete registration function with Android channel setup and error handling. - 04
Pattern 1: Permission Request Flow
Always check existing permission status before prompting. On iOS, the permission dialog can only be shown ONCE natively -- if denied, you must direct users to Settings.
Always check existing permission status before prompting. On iOS, the permission dialog can only be shown ONCE natively -- if denied, you must direct users to Settings.Why good: checks existing status first to avoid redundant prompts, handles denial gracefully, works on both platformsSee examples/core.md for the complete registration function with Android channel setup and error handling. - 05
Pattern 2: Push Token Retrieval
Expo push tokens work with Expo's push service. Native device tokens (FCM/APNs) work with your own backend or third-party services.
Expo push tokens work with Expo's push service. Native device tokens (FCM/APNs) work with your own backend or third-party services.Why good: projectId is explicit (not inferred), both token types available depending on backend choiceSee examples/core.md for token refresh handling and Firebase token retrieval patterns.
Permission review
Static risk signals and limitations
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
Why each signal appears
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 93/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 23 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Provenance and original SKILL.md
- Repository
- agents-inc/skills
- Skill path
- src/skills/mobile-notifications-push/SKILL.md
- Commit
- 81d43a51211aca12c85dcc16085fa99014ec548e
- License
- MIT
- Collected
- 2026-08-28
- Default branch
- main
View the original SKILL.md
Push Notification Patterns
Quick Guide: Two main approaches:
expo-notifications(Expo workflow, unified API for push + local) and@react-native-firebase/messaging(bare RN, FCM/APNs direct). Always request permissions before retrieving tokens. Handle three notification states: foreground (app open), background (app minimized), and quit (app killed). Set up Android notification channels before displaying any notification. Push notifications require a physical device -- they do not work on emulators or simulators.
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST request notification permissions BEFORE retrieving push tokens -- calling getExpoPushTokenAsync or messaging().getToken() without permission will fail or return an unusable token)
(You MUST create an Android notification channel BEFORE displaying any notification on Android 8+ -- notifications without a channel are silently dropped)
(You MUST handle ALL three notification states: foreground (onMessage/addNotificationReceivedListener), background (setBackgroundMessageHandler/registerTaskAsync), and tap/response (addNotificationResponseReceivedListener/onNotificationOpenedApp + getInitialNotification))
(You MUST clean up notification listeners on unmount -- leaked listeners cause memory leaks and duplicate handlers)
(You MUST test push notifications on a physical device -- emulators and simulators do not support push tokens)
</critical_requirements>
Auto-detection: expo-notifications, @react-native-firebase/messaging, push notification, push token, getExpoPushTokenAsync, getDevicePushTokenAsync, scheduleNotificationAsync, setNotificationHandler, addNotificationReceivedListener, addNotificationResponseReceivedListener, setBackgroundMessageHandler, onMessage, onNotificationOpenedApp, getInitialNotification, notification channel, setNotificationChannelAsync, notification category, notification actions, setBadgeCountAsync, FCM, APNs, remote notification, local notification, useLastNotificationResponse
When to use:
- Sending remote push notifications to users via FCM/APNs
- Requesting and managing notification permissions
- Retrieving and storing push tokens (Expo push token or native FCM/APNs token)
- Handling notification events in foreground, background, and quit states
- Scheduling local notifications (reminders, timers, recurring alerts)
- Creating Android notification channels with custom sound/vibration/importance
- Adding interactive notification actions (buttons, text input)
- Managing app icon badge counts
- Implementing notification tap navigation (deep linking from notifications)
Key patterns covered:
- Permission request flow with status checking
- Push token retrieval and refresh handling
- Foreground notification presentation (setNotificationHandler)
- Background message handling (headless JS tasks)
- Notification tap/response handling with navigation
- Local notification scheduling with trigger types
- Android notification channels and channel groups
- Notification categories with interactive actions
- Badge count management
- Rich notification content (images, sounds, data payloads)
When NOT to use:
- In-app messaging or toast/snackbar UI (those are UI components, not OS notifications)
- Email or SMS notifications (server-side concern)
- Web push notifications (different API entirely)
Detailed Resources:
- examples/core.md - Permission flow, token management, foreground/background/tap listeners, notification handler setup
- examples/scheduling.md - Local notification scheduling, trigger types, Android channels, categories and actions, badge management
- reference.md - Decision frameworks, platform differences, checklists
Philosophy
Push notifications bridge the gap between your app and users when the app is not in focus. The two main approaches in React Native serve different workflows:
expo-notifications provides a unified API for both push and local notifications, abstracts FCM/APNs differences, and integrates with Expo's push service for simplified server-side sending. Best for Expo-managed and bare workflows that want a single library for all notification needs.
@react-native-firebase/messaging provides direct FCM integration, pairs naturally with Firebase's backend services, and is the standard for bare React Native projects already using Firebase. For displaying foreground notifications with Firebase, pair it with a local notification display library.
Core principles:
- Permission first -- Always check and request permissions before any token or notification work. Requesting at a contextually appropriate moment (after user sees value) dramatically improves grant rates.
- Handle all three states -- Notifications arrive when the app is in foreground, background, or quit. Each state requires a different listener. Missing one means silently lost notifications.
- Channels are mandatory on Android -- Android 8+ (API 26+) requires notification channels. Without one, notifications are silently dropped. Create channels at app startup, not at send time.
- Tokens change -- Push tokens can rotate. Register a token refresh listener and update your backend whenever the token changes.
- Physical device required -- Push notification infrastructure (FCM/APNs) does not work on emulators or simulators. Local notifications may work on simulators but push tokens will not.
Mental model:
Server sends push -> FCM/APNs delivers to device -> OS displays notification
| |
| (or Expo Push Service abstracts FCM/APNs) |
| v
| User taps notification
| |
v v
App in foreground: App opens with payload:
-> onMessage / notificationReceived -> response listener
-> YOU decide whether to show it -> navigate to content
Core Patterns
Pattern 1: Permission Request Flow
Always check existing permission status before prompting. On iOS, the permission dialog can only be shown ONCE natively -- if denied, you must direct users to Settings.
// expo-notifications approach
import * as Notifications from "expo-notifications";
import * as Device from "expo-device";
import { Platform } from "react-native";
const { status: existingStatus } = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== "granted") {
// Handle denial -- direct to Settings or degrade gracefully
return;
}
Why good: checks existing status first to avoid redundant prompts, handles denial gracefully, works on both platforms
See examples/core.md for the complete registration function with Android channel setup and error handling.
Pattern 2: Push Token Retrieval
Expo push tokens work with Expo's push service. Native device tokens (FCM/APNs) work with your own backend or third-party services.
// Expo push token -- for use with Expo Push Service
const expoPushToken = await Notifications.getExpoPushTokenAsync({
projectId:
Constants?.expoConfig?.extra?.eas?.projectId ??
Constants?.easConfig?.projectId,
});
// Returns: "ExponentPushToken[xxxxxx]"
// Native device token -- for direct FCM/APNs integration
const deviceToken = await Notifications.getDevicePushTokenAsync();
// Returns: { type: "ios" | "android", data: "native-token-string" }
Why good: projectId is explicit (not inferred), both token types available depending on backend choice
See examples/core.md for token refresh handling and Firebase token retrieval patterns.
Pattern 3: Foreground Notification Handler
By default, notifications received while the app is in the foreground are NOT displayed. You must explicitly opt in via setNotificationHandler.
// Call once at app startup (outside of any component)
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldPlaySound: true,
shouldSetBadge: true,
shouldShowBanner: true, // replaces deprecated shouldShowAlert
shouldShowList: true, // show in notification center
}),
});
Why good: explicit opt-in to foreground display, uses current API (shouldShowBanner/shouldShowList, not deprecated shouldShowAlert), called at module scope so it runs before any notification arrives
Gotcha: shouldShowAlert is deprecated in recent expo-notifications versions -- use shouldShowBanner and shouldShowList instead.
See examples/core.md for conditional foreground handling (e.g., suppressing notification when user is already on that screen).
Pattern 4: Notification Listeners (Foreground, Background, Tap)
Three distinct handlers cover the full notification lifecycle.
// Foreground: notification arrives while app is open
const receivedSub = Notifications.addNotificationReceivedListener(
(notification) => {
const data = notification.request.content.data;
// Update UI, show in-app indicator, etc.
},
);
// Tap/Response: user taps a notification (from any state)
const responseSub = Notifications.addNotificationResponseReceivedListener(
(response) => {
const data = response.notification.request.content.data;
// Navigate to relevant screen
},
);
// Cleanup on unmount
return () => {
receivedSub.remove();
responseSub.remove();
};
Why good: separate listeners for receiving vs tapping, cleanup prevents leaks, data extraction from correct nested path
See examples/core.md for the complete useNotificationListeners hook, background handler registration, and Firebase equivalents.
Pattern 5: Android Notification Channels
Required on Android 8+ (API 26+). Create channels at app startup. Users can customize channel settings (sound, vibration) in system settings -- your code cannot override user preferences after creation.
const CHANNELS = {
messages: {
id: "messages",
name: "Messages",
importance: Notifications.AndroidImportance.HIGH,
},
updates: {
id: "updates",
name: "App Updates",
importance: Notifications.AndroidImportance.DEFAULT,
},
marketing: {
id: "marketing",
name: "Promotions",
importance: Notifications.AndroidImportance.LOW,
},
} as const;
// Create at app startup
if (Platform.OS === "android") {
await Notifications.setNotificationChannelAsync(CHANNELS.messages.id, {
name: CHANNELS.messages.name,
importance: CHANNELS.messages.importance,
vibrationPattern: [0, 250, 250, 250],
lightColor: "#FF231F7C",
sound: "default",
});
}
Why good: channels defined as constants, importance levels match notification priority, created at startup before any notification arrives
See examples/scheduling.md for channel groups and channel management patterns.
Pattern 6: Local Notification Scheduling
Schedule notifications for future delivery without a server. Supports one-time, repeating, and calendar-based triggers.
const REMINDER_DELAY_SECONDS = 60;
await Notifications.scheduleNotificationAsync({
content: {
title: "Reminder",
body: "Don't forget to complete your task!",
data: { screen: "tasks", taskId: "abc123" },
sound: "default",
},
trigger: {
type: Notifications.SchedulableTriggerInputTypes.TIME_INTERVAL,
seconds: REMINDER_DELAY_SECONDS,
},
});
Why good: data payload enables navigation on tap, trigger type is explicit, named constant for delay
See examples/scheduling.md for daily/weekly recurring triggers, calendar triggers, and platform-specific trigger differences.
Pattern 7: Notification Categories and Actions
Categories define interactive buttons and text input fields on notifications. Register categories at app startup.
await Notifications.setNotificationCategoryAsync("message", [
{
identifier: "reply",
buttonTitle: "Reply",
textInput: { submitButtonTitle: "Send", placeholder: "Type a reply..." },
},
{
identifier: "mark-read",
buttonTitle: "Mark as Read",
options: { opensAppToForeground: false },
},
]);
Why good: text input action for quick replies, opensAppToForeground: false for silent actions, registered at startup before notifications arrive
See examples/scheduling.md for handling action responses and iOS-specific category options.
<decision_framework>
Decision Framework
Key decisions: which library (expo-notifications vs @react-native-firebase/messaging), which push token type (Expo vs native), which trigger type for local notifications, and which Android channel importance level.
See reference.md for complete decision trees, platform differences table, and channel importance reference.
</decision_framework>
<red_flags>
RED FLAGS
High Priority Issues:
- Requesting push token before checking/requesting permissions -- fails silently or returns unusable token on iOS
- Missing Android notification channel creation -- notifications silently dropped on Android 8+ (API 26+)
- Not handling the "quit" state --
getInitialNotification()(Firebase) oruseLastNotificationResponse()(Expo) is the only way to get the notification that launched the app - Using
shouldShowAlertinstead ofshouldShowBanner/shouldShowList-- deprecated API, will break in future expo-notifications versions - Not cleaning up listeners on unmount -- causes memory leaks and duplicate notification handlers
- Testing only on simulator/emulator -- push tokens and remote notifications require a physical device
Medium Priority Issues:
- Hardcoding push token on the server without refresh handling -- tokens rotate and become invalid
- Creating notification channels at notification send time instead of app startup -- causes race condition where first notification is dropped
- Using
console.login background handlers -- headless JS tasks may not have console access; use your logging solution - Not sending
channelIdin Android notification payloads -- notification uses default channel, ignoring your custom channel settings - Requesting permission immediately on app launch -- users deny at higher rates without context; request after demonstrating value
Gotchas & Edge Cases:
- iOS permission dialog shows only ONCE natively -- if denied, subsequent
requestPermissionsAsync()calls return "denied" without showing a dialog; direct users to Settings - Expo SDK 53+ dropped push notification support from Expo Go on Android -- you need a development build to test
setBackgroundMessageHandler(Firebase) andregisterTaskAsync(Expo) must be called at the TOP LEVEL of your entry file (index.js), not inside a component- Firebase data-only messages require
priority: "high"(Android) andcontent-available: 1(iOS) to trigger background handlers - Android notification icons must be white with transparent background -- colored icons render as solid white squares
DailyTriggerandWeeklyTriggerare Android-only in expo-notifications -- useCalendarTriggerfor iOS- Notification categories/actions may not show in background/killed state on some Android devices (known limitation)
- Both
expo-notificationsand@react-native-firebase/messagingregister for the same Android FCM intents -- using both requires manual conflict resolution getInitialNotification()returns null if called too late -- call it early in app initialization, not after navigation is ready
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md
(You MUST request notification permissions BEFORE retrieving push tokens -- calling getExpoPushTokenAsync or messaging().getToken() without permission will fail or return an unusable token)
(You MUST create an Android notification channel BEFORE displaying any notification on Android 8+ -- notifications without a channel are silently dropped)
(You MUST handle ALL three notification states: foreground (onMessage/addNotificationReceivedListener), background (setBackgroundMessageHandler/registerTaskAsync), and tap/response (addNotificationResponseReceivedListener/onNotificationOpenedApp + getInitialNotification))
(You MUST clean up notification listeners on unmount -- leaked listeners cause memory leaks and duplicate handlers)
(You MUST test push notifications on a physical device -- emulators and simulators do not support push tokens)
Failure to follow these rules will result in silently dropped notifications, missed user interactions, and platform-specific failures that are difficult to debug.
</critical_reminders>
Frequently asked questions
What to verify before installation and use
What does the mobile-notifications-push source document cover?
Quick Guide: Two main approaches: expo-notifications (Expo workflow, unified API for push + local) and @react-native-firebase/messaging (bare RN, FCM/APNs direct). Always request permissions before retrieving tokens. Handle three notification states: foreground (app open), backg…
How do I install mobile-notifications-push?
The source record exposes this install command: npx skills add https://github.com/agents-inc/skills --skill "src/skills/mobile-notifications-push". Inspect the command and pinned source before running it.
Alternatives
Compare before choosing
event4u-app/agent-config
existing-ui-audit
Use BEFORE writing or editing any non-trivial UI — inventories components, design tokens, shadcn primitives, and reusable patterns into state.ui_audit. Hard gate for the ui directive set.
kensaurus/cursor-kenji
enhance-web-web3d
Add purposeful 3D/WebGL and scroll choreography to an existing site with Three.js/R3F, GSAP, or Motion. Use when "add 3D", "WebGL hero", "React Three Fiber", or "scroll-driven 3D". General UI polish → enhance-web-ui. Motion without 3D → enhance-motion.
UiPath/skills
uipath-coded-apps
UiPath Coded Apps — scaffold, build, run, and deploy Coded Web Apps and Coded Action Apps: React/TypeScript apps that call UiPath Cloud APIs via the `@uipath/uipath-typescript` SDK and ship to Automation Cloud (push/pull to Studio Web, pack, publish, deploy, OAuth-PKCE). Also generates live analytics & governance dashboards from a plain-language request, wired to tenant data via the Insights real-time API, with edit and deploy flows. For RPA→uipath-rpa, Python agents→uipath-agents, Maestro flows
hyperfx-ai/marketing-skills
slack
Slack messaging, file sharing, Block Kit formatting, and channel management. Use when the user wants to send Slack messages, post rich Block Kit layouts, share files, react, or manage channels and members.