Skip to main content
Glama
notifications.md21.6 kB
--- title: Notifications description: A library that provides an API to fetch push notification tokens and to present, schedule, receive and respond to notifications. sourceCodeUrl: https://github.com/expo/expo/tree/sdk-53/packages/expo-notifications packageName: expo-notifications iconUrl: /static/images/packages/expo-notifications.png platforms: ["android*", "ios*"] --- `expo-notifications` provides an API to fetch push notification tokens and to present, schedule, receive and respond to notifications. > **warning** Push notifications (remote notifications) functionality provided by `expo-notifications` is unavailable in Expo Go on Android from SDK 53. A [development build](/develop/development-builds/introduction/) is required to use push notifications. Local notifications (in-app notifications) remain available in Expo Go. ## Features - Schedule a one-off notification for a specific date or some time from now - Schedule a notification repeating in some time interval (or a calendar date match on iOS) - Get and set the application badge icon number - Obtain a native device push token, so you can send push notifications with FCM (for Android) and APNs (for iOS) - Obtain an Expo push token, so you can send push notifications with [Expo Push Service](/push-notifications/sending-notifications/) - Listen to incoming notifications in the foreground and background - Listen to interactions with notifications - Handle notifications when the app is in the foreground - Imperatively dismiss notifications from Notification Center/tray - Create, update, and delete [Android notification channels](https://developer.android.com/develop/ui/views/notifications/channels) - Set custom icon and color for notifications on Android ## Installation <br /> Then proceed to [configuration](#configuration) to set up the [config plugin](#app-config) and obtain the [credentials](#credentials) for push notifications. ## Usage Check out the example Snack below to see Notifications in action, make sure to use a physical device to test it. Push notifications don't work on emulators/simulators. ```tsx Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldPlaySound: false, shouldSetBadge: false, shouldShowBanner: true, shouldShowList: true, }), }); const [expoPushToken, setExpoPushToken] = useState(''); const [channels, setChannels] = useState<Notifications.NotificationChannel[]>([]); const [notification, setNotification] = useState<Notifications.Notification | undefined>( undefined ); useEffect(() => { registerForPushNotificationsAsync().then(token => token && setExpoPushToken(token)); if (Platform.OS === 'android') { Notifications.getNotificationChannelsAsync().then(value => setChannels(value ?? [])); } const notificationListener = Notifications.addNotificationReceivedListener(notification => { setNotification(notification); }); const responseListener = Notifications.addNotificationResponseReceivedListener(response => { console.log(response); }); return () => { notificationListener.remove(); responseListener.remove(); }; }, []); return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'space-around', }}> <Text>Your expo push token: {expoPushToken}</Text> <Text>{`Channels: ${JSON.stringify( channels.map(c => c.id), null, 2 )}`}</Text> <View style={{ alignItems: 'center', justifyContent: 'center' }}> <Text>Title: {notification && notification.request.content.title} </Text> <Text>Body: {notification && notification.request.content.body}</Text> <Text>Data: {notification && JSON.stringify(notification.request.content.data)}</Text> </View> <Button title="Press to schedule a notification" onPress={async () => { await schedulePushNotification(); }} /> </View> ); } async function schedulePushNotification() { await Notifications.scheduleNotificationAsync({ content: { title: "You've got mail! 📬", body: 'Here is the notification body', data: { data: 'goes here', test: { test1: 'more data' } }, }, trigger: { type: Notifications.SchedulableTriggerInputTypes.TIME_INTERVAL, seconds: 2, }, }); } async function registerForPushNotificationsAsync() { let token; if (Platform.OS === 'android') { await Notifications.setNotificationChannelAsync('myNotificationChannel', { name: 'A channel is needed for the permissions prompt to appear', importance: Notifications.AndroidImportance.MAX, vibrationPattern: [0, 250, 250, 250], lightColor: '#FF231F7C', }); } if (Device.isDevice) { const { status: existingStatus } = await Notifications.getPermissionsAsync(); let finalStatus = existingStatus; if (existingStatus !== 'granted') { const { status } = await Notifications.requestPermissionsAsync(); finalStatus = status; } if (finalStatus !== 'granted') { alert('Failed to get push token for push notification!'); return; } // Learn more about projectId: // https://docs.expo.dev/push-notifications/push-notifications-setup/#configure-projectid // EAS projectId is used here. try { const projectId = Constants?.expoConfig?.extra?.eas?.projectId ?? Constants?.easConfig?.projectId; if (!projectId) { throw new Error('Project ID not found'); } token = ( await Notifications.getExpoPushTokenAsync({ projectId, }) ).data; console.log(token); } catch (e) { token = `${e}`; } } else { alert('Must use physical device for Push Notifications'); } return token; } ``` ### Present a local (in-app) notification to the user ```ts // First, set the handler that will cause the notification // to show the alert Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowBanner: true, shouldShowList: true, shouldPlaySound: false, shouldSetBadge: false, }), }); // Second, call scheduleNotificationAsync() Notifications.scheduleNotificationAsync({ content: { title: 'Look at that notification', body: "I'm so proud of myself!", }, trigger: null, }); ``` ### Handle push notifications with navigation If you'd like to deep link to a specific screen in your app when you receive a push notification, you can configure either of Expo's navigation systems to do that. <Tab label="Expo Router"> You can use Expo Router's [built-in deep linking](/router/basics/core-concepts/#2-all-pages-have-a-url) to handle incoming URLs from push notifications. Simply configure the root layout to listen for incoming and initial notification events. ```tsx app/_layout.tsx function useNotificationObserver() { useEffect(() => { let isMounted = true; function redirect(notification: Notifications.Notification) { const url = notification.request.content.data?.url; if (url) { /* @info Push the URL. You may want to verify the format before navigating. */ router.push(url); /* @end */ } } /* @info Handle the initial push notification. */ Notifications.getLastNotificationResponseAsync() /* @end */ .then(response => { if (!isMounted || !response?.notification) { return; } redirect(response?.notification); }); /* @info Listen for runtime notifications. */ const subscription = Notifications.addNotificationResponseReceivedListener(response => { /* @end */ redirect(response.notification); }); return () => { isMounted = false; subscription.remove(); }; }, []); } /* @info Observe at the root. Ensure this layout never returns <b>null</b> or the navigation will go unhandled. */ useNotificationObserver(); /* @end */ return ; } ``` </Tab> <Tab label="React Navigation"> React Navigation's manual [linking configuration](https://reactnavigation.org/docs/navigation-container#linking) can be configured to handle incoming redirects from push notifications: ```tsx App.tsx return ( <NavigationContainer linking={{ config: { // Configuration for linking }, async getInitialURL() { // First, you may want to do the default deep link handling // Check if app was opened from a deep link const url = await Linking.getInitialURL(); if (url != null) { return url; } // Handle URL from expo push notifications const response = await Notifications.getLastNotificationResponseAsync(); return response?.notification.request.content.data.url; }, subscribe(listener) { const onReceiveURL = ({ url }: { url: string }) => listener(url); // Listen to incoming links from deep linking const eventListenerSubscription = Linking.addEventListener('url', onReceiveURL); // Listen to expo push notifications const subscription = Notifications.addNotificationResponseReceivedListener(response => { const url = response.notification.request.content.data.url; // Any custom logic to see whether the URL needs to be handled //... // Let React Navigation handle the URL listener(url); }); return () => { // Clean up the event listeners eventListenerSubscription.remove(); subscription.remove(); }; }, }}> {/* Your app content */} </NavigationContainer> ); } ``` See more details on [React Navigation documentation](https://reactnavigation.org/docs/deep-linking/#third-party-integrations). </Tab> ## Configuration ### Credentials Follow the [setup guide](/push-notifications/push-notifications-setup/#get-credentials-for-development-builds). ### App config To configure `expo-notifications`, use the built-in [config plugin](/config-plugins/introduction/) in the app config (**app.json** or **app.config.js**) for [EAS Build](/build/introduction) or with `npx expo run:[android|ios]`. The plugin allows you to configure the following properties that cannot be set at runtime and require building a new app binary to take effect: Here is an example of using the config plugin in the app config file: ```json app.json { "expo": { "plugins": [ [ "expo-notifications", { "icon": "./local/assets/notification_icon.png", "color": "#ffffff", "defaultChannel": "default", "sounds": [ "./local/assets/notification_sound.wav", "./local/assets/notification_sound_other.wav" ], "enableBackgroundRemoteNotifications": false } ] ] } } ``` > The iOS APNs entitlement is _always_ set to 'development'. Xcode automatically changes this to 'production' in the archive generated by a release build. > [Learn more](https://stackoverflow.com/a/42293632/4047926). Learn how to configure the native projects in the [installation instructions in the `expo-notifications` repository](https://github.com/expo/expo/tree/main/packages/expo-notifications#installation-in-bare-react-native-projects). ## Permissions ### Android - On Android, this module requires permission to subscribe to the device boot. It's used to set up scheduled notifications when the device (re)starts. The `RECEIVE_BOOT_COMPLETED` permission is added automatically through the library's **AndroidManifest.xml**. - Starting from Android 12 (API level 31), to schedule a notification that triggers at an exact time, you need to add `<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>` to **AndroidManifest.xml**. Read more about the [exact alarm permission](https://developer.android.com/about/versions/12/behavior-changes-12#exact-alarm-permission). - On Android 13, app users must opt-in to receive notifications via a permissions prompt automatically triggered by the operating system. This prompt will not appear until at least one notification channel is created. The `setNotificationChannelAsync` must be called before `getDevicePushTokenAsync` or `getExpoPushTokenAsync` to obtain a push token. You can read more about the new notification permission behavior for Android 13 in the [official documentation](https://developer.android.com/develop/ui/views/notifications/notification-permission#new-apps). ### iOS No usage description is required, see [notification-related permissions](#fetch-information-about-notifications-related-permissions). ### Interpret the iOS permissions response On iOS, permissions for sending notifications are a little more granular than they are on Android. This is why you should rely on the `NotificationPermissionsStatus`'s `ios.status` field, instead of the root `status` field. This value will be one of the following, accessible under `Notifications.IosAuthorizationStatus`: - `NOT_DETERMINED`: The user hasn't yet made a choice about whether the app is allowed to schedule notifications - `DENIED`: The app isn't authorized to schedule or receive notifications - `AUTHORIZED`: The app is authorized to schedule or receive notifications - `PROVISIONAL`: The app is provisionally authorized to post noninterruptive user notifications - `EPHEMERAL`: The app is authorized to schedule or receive notifications for a limited amount of time ## Notification events listeners Notification events include incoming notifications, interactions your users perform with notifications (this can be tapping on a notification, or interacting with it via [notification categories](#manage-notification-categories-interactive-notifications)), and rare occasions when your notifications may be dropped. Several listeners are exposed and documented in the [Push notification behaviors](/push-notifications/what-you-need-to-know/#push-notification-behaviors) section. ## Headless (Background) notifications See the [definition](/push-notifications/what-you-need-to-know/#headless-background-notifications) of Headless Background Notifications in the [What you need to know](/push-notifications/what-you-need-to-know) guide. To handle notifications while the app is in the background or not running, you need to do the following: - Add `expo-task-manager` package to your project. - [Configure background notifications](#background-notification-configuration). - In your application code, set up a [background task](#registertaskasynctaskname) to run when the notification is received. Then send a push notification which: - Contains only the `data` key (no `title`, `body`) - Has `_contentAvailable: true` set for iOS &mdash; see the [Expo push notification service payload format](/push-notifications/sending-notifications/#message-request-format) ### Background notification configuration&ensp; To be able to use background push notifications on iOS, the `remote-notification` value needs to be present in the `UIBackgroundModes` array in your app's **Info.plist** file. **If you're using [CNG](/workflow/continuous-native-generation/)**, set the [`enableBackgroundRemoteNotifications` property](#configurable-properties) of the config plugin to true, and the correct configuration will be applied automatically by prebuild. If you're not using Continuous Native Generation ([CNG](/workflow/continuous-native-generation/)) or you're using a native iOS project, then you'll need to add the following to your **Expo.plist** file: ```xml ios/project-name/Supporting/Expo.plist <key>UIBackgroundModes</key> <array> <string>remote-notification</string> </array> ``` ## Additional information ### Set custom notification sounds To add custom push notification sounds to your app, add the `expo-notifications` plugin to your **app.json** file and then under the `sounds` key, provide an array of local paths to sound files that can be used as custom notification sounds. These local paths are local to your project. ```json app.json { "expo": { "plugins": [ [ "expo-notifications", { "sounds": ["local/path/to/mySoundFile.wav"] } ] ] } } ``` After building your app, the array of files will be available for use in both [`NotificationContentInput`](#notificationcontentinput) and [`NotificationChannelInput`](#notificationchannelinput). You only need to provide the base filename. Here's an example using the config above: ```ts await Notifications.setNotificationChannelAsync('new_emails', { name: 'E-mail notifications', importance: Notifications.AndroidImportance.HIGH, sound: 'mySoundFile.wav', // Provide ONLY the base filename }); await Notifications.scheduleNotificationAsync({ content: { title: "You've got mail! 📬", sound: 'mySoundFile.wav', // Provide ONLY the base filename }, trigger: { type: Notifications.SchedulableTriggerInputTypes.TIME_INTERVAL, seconds: 2, channelId: 'new_emails', }, }); ``` You can also manually add notification files to your Android and iOS projects if you prefer: On Androids 8.0+, playing a custom sound for a notification requires more than setting the `sound` property on the `NotificationContentInput`. You will also need to configure the `NotificationChannel` with the appropriate `sound`, and use it when sending/scheduling the notification. For the example below to work, you would place your **email_sound.wav** file in **android/app/src/main/res/raw/**. ```ts // Prepare the notification channel await Notifications.setNotificationChannelAsync('new_emails', { name: 'E-mail notifications', importance: Notifications.AndroidImportance.HIGH, sound: 'email_sound.wav', // <- for Android 8.0+, see channelId property below }); // Eg. schedule the notification await Notifications.scheduleNotificationAsync({ content: { title: "You've got mail! 📬", body: 'Open the notification to read them all', sound: 'email_sound.wav', // <- for Android below 8.0 }, trigger: { type: Notifications.SchedulableTriggerInputTypes.TIME_INTERVAL, seconds: 2, channelId: 'new_emails', // <- for Android 8.0+, see definition above }, }); ``` On iOS, all that's needed is to place your sound file in your Xcode project (see the screenshot below), and then specify the sound file in your `NotificationContentInput`, like this: ```ts await Notifications.scheduleNotificationAsync({ content: { title: "You've got mail! 📬", body: 'Open the notification to read them all', sound: 'notification.wav', }, trigger: { // ... }, }); ``` ### Push notification payload specification See [Message request format](/push-notifications/sending-notifications/#message-request-format). ### Manage notification categories for interactive notifications Notification categories allow you to create interactive push notifications, so that a user can respond directly to the incoming notification either via buttons or a text response. A category defines the set of actions a user can take, and then those actions are applied to a notification by specifying the `categoryIdentifier` in the [`NotificationContent`](#notificationcontent). On iOS, notification categories also allow you to customize your notifications further. With each category, not only can you set interactive actions a user can take, but you can also configure things like the placeholder text to display when the user disables notification previews for your app. ## Platform-specific guides ### Handling notification channels&ensp; Starting in Android 8.0 (API level 26), all notifications must be assigned to a channel. For each channel, you can set the visual and auditory behavior that is applied to all notifications in that channel. Then, users can change these settings and decide which notification channels from your app should be intrusive or visible at all, as [Android developer docs](https://developer.android.com/training/notify-user/channels) states. If you do not specify a notification channel, `expo-notifications` will create a fallback channel for you, named **Miscellaneous**. We encourage you to always ensure appropriate channels with informative names are set up for the application and to always send notifications to these channels. > Calling these methods is a no-op for platforms that do not support this feature (Android below version 8.0 (26) and iOS). ### Custom notification icon and colors&ensp; You can configure the [`notification.icon`](../config/app/#notification) and [`notification.color`](../config/app/#notification) keys in the project's **app.json** if you are using [Expo Prebuild](/workflow/prebuild) or by using the [`expo-notifications` config plugin directly](#configurable-properties). These are build-time settings, so you'll need to recompile your native Android app with `eas build -p android` or `npx expo run:android` to see the changes. For your notification icon, make sure you follow [Google's design guidelines](https://material.io/design/iconography/product-icons.html#design-principles) (the icon must be all white with a transparent background) or else it may not be displayed as intended. You can also set a custom notification color **per-notification** directly in your [`NotificationContentInput`](#notificationcontentinput) under the `color` attribute. ## API ```js ```

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jaksm/expo-docs-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server