Skip to main content
Glama
notifications.md22.8 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-51/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` will be unavailable in Expo Go from SDK 53. Instead, use a [development build](/develop/development-builds/introduction/). Scheduled notifications (local notifications) will 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 - Fetch a native device push token, so you can send push notifications with FCM and APNs - Fetch an Expo push token, so you can send push notifications with Expo - 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 - Set custom icon and color for notifications on Android ## Installation ## 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 () => ({ shouldShowAlert: true, shouldPlaySound: false, shouldSetBadge: false, }), }); const [expoPushToken, setExpoPushToken] = useState(''); const [channels, setChannels] = useState<Notifications.NotificationChannel[]>([]); const [notification, setNotification] = useState<Notifications.Notification | undefined>( undefined ); const notificationListener = useRef<Notifications.Subscription>(); const responseListener = useRef<Notifications.Subscription>(); useEffect(() => { registerForPushNotificationsAsync().then(token => token && setExpoPushToken(token)); if (Platform.OS === 'android') { Notifications.getNotificationChannelsAsync().then(value => setChannels(value ?? [])); } notificationListener.current = Notifications.addNotificationReceivedListener(notification => { setNotification(notification); }); responseListener.current = Notifications.addNotificationResponseReceivedListener(response => { console.log(response); }); return () => { notificationListener.current && Notifications.removeNotificationSubscription(notificationListener.current); responseListener.current && Notifications.removeNotificationSubscription(responseListener.current); }; }, []); 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: { seconds: 2 }, }); } async function registerForPushNotificationsAsync() { let token; if (Platform.OS === 'android') { await Notifications.setNotificationChannelAsync('default', { name: 'default', 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 the notification to the user ```ts // First, set the handler that will cause the notification // to show the alert Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowAlert: true, shouldPlaySound: false, shouldSetBadge: false, }), }); // Second, call the method 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 #### Android Firebase Cloud Messaging credentials are required for all Android apps to receive push notifications in your app (except when testing in Expo Go). For more information, see how to get [FCM credentials](/push-notifications/push-notifications-setup/#android) for your app. #### iOS To register your iOS device and automatically enable push notifications for your EAS Build, see [push notification setup](/push-notifications/push-notifications-setup/#ios). ### 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" ] } ] ] } } ``` > The iOS APNs entitlement is _always_ set to 'development'. Xcode automatically changes this to 'production' during the archive. > [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/sdk-51/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 setup 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 the notification that triggers at the exact time, you need to add `<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>` to **AndroidManifest.xml**. You can 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](#fetching-information-about-notifications-related-permissions). ## 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](#managing-notification-categories-interactive-notifications)), and rare occasions when your notifications may be dropped. A few different listeners are exposed, so we've provided a chart below which will hopefully help you understand when you can expect each one to be triggered: | User interacted with notification? | App state | Listener(s) triggered | | :--------------------------------- | :----------------------: | ----------------------------------------------------------------------- | | false | Foreground | `NotificationReceivedListener` | | false | Foreground or Background | `BackgroundNotificationTask` | | false | Killed | none | | true | Foreground | `NotificationReceivedListener` & `NotificationResponseReceivedListener` | | true | Background | `NotificationResponseReceivedListener` | | true | Killed | `useLastNotificationResponse` or `getLastNotificationResponseAsync` | In the table above, whenever `NotificationResponseReceivedListener` is triggered, the same would apply to the `useLastNotificationResponse` hook. > **info** When the app is not running or killed and is restarted by tapping on a notification, the `NotificationResponseReceivedListener` may or may not be triggered. To reliably capture the response, we recommend using `useLastNotificationResponse` or `getLastNotificationResponseAsync`. ### Background notifications > **warning** Background event listeners are not supported in Expo Go. A background notification (iOS) or a data-only notification (Android) is a remote notification that does not display an alert, play a sound, or add a badge to your app's icon. The purpose of a background notification is to provide a way to wake up your app to trigger an app data refresh in the background. 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. - In your application code, set up a [background task](#registertaskasynctaskname) to run when the notification is received. For Android: - Send a push notification payload containing only the `data` key. For iOS: - In the array under the `ios.infoPlist.UIBackgroundModes` key in your [app config](/workflow/configuration/), add the values `remote-notification` and `processing`. - Add `_contentAvailable: true` to your push notification payload for the Expo push notification service. Under normal circumstances, the "content-available" flag should launch your app in the background. ## Additional information ### Set custom notification sounds Custom notification sounds are only supported when using [EAS Build](/build/introduction). 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: { 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: { 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: { // ... }, }); ``` ### Android push notification payload specification When sending a push notification, put an object conforming to the following type as `data` of the notification: ```ts title?: string; message?: string; subtitle?: string; sound?: boolean | string; vibrate?: boolean | number[]; badge?: number; } ``` ### Interpret the iOS permissions response On iOS, permissions for sending notifications are a little more granular than they are on Android. Because of this, 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 application is provisionally authorized to post noninterruptive user notifications - `EPHEMERAL`: The app is authorized to schedule or receive notifications for a limited amount of time ### Manage notification categories (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