This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.

Expo Notifications iconExpo Notifications

A library that provides an API to fetch push notification tokens and to present, schedule, receive and respond to notifications.

Android
iOS
Recommended version:
~56.0.26

expo-notifications provides an API to fetch push notification tokens and to present, schedule, receive and respond to notifications.

Notification guides

Do not miss our guides on how to set up, send, and handle push notifications.

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
  • 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

Terminal
- npx expo install expo-notifications
- yarn expo install expo-notifications
- pnpm expo install expo-notifications
- bun expo install expo-notifications

If you are installing this in an existing React Native app, make sure to install expo in your project.


Then proceed to configuration to set up the config plugin and obtain the credentials for push notifications.

Known issues
Android

When launching the app from a push notification in Android development builds, the splash screen may fail to display correctly about 70% of the time. The icon and fade animation may not appear as expected.

  • Icon may be missing
  • Fade animation may not run
  • Only the background color may flash briefly

This issue only affects debug builds and does not occur in release builds. To workaround it, test notification launches in release mode (npx expo run:android --variant release) for accurate behavior.

Usage

Check out the example Snack below to see Notifications in action. Push notifications work on physical devices, Android emulators with Google Play services, and iOS simulators on Xcode 14 or later (macOS 13+, iOS 16+).

Push Notifications
import { useState, useEffect } from 'react'; import { Text, View, Button, Platform } from 'react-native'; import * as Notifications from 'expo-notifications'; import Constants from 'expo-constants'; Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldPlaySound: false, shouldSetBadge: false, shouldShowBanner: true, shouldShowList: true, }), }); export default function App() { 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', }); } 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}`; } return token; }

Present a local (in-app) notification to the user

import * as Notifications from 'expo-notifications'; // First, set the handler that will cause the notification // to show the alert Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldPlaySound: false, shouldSetBadge: false, shouldShowBanner: true, shouldShowList: true, }), }); // 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.

You can use Expo Router's built-in deep linking to handle incoming URLs from push notifications. Simply configure the root layout to listen for incoming and initial notification events.

app/_layout.tsx
import { useEffect } from 'react'; import * as Notifications from 'expo-notifications'; import { router } from 'expo-router'; function useNotificationObserver() { useEffect(() => { function redirect(notification: Notifications.Notification) { const url = notification.request.content.data?.url; if (typeof url === 'string') { router.push(url); } } const response = Notifications.getLastNotificationResponse(); if (response?.notification) { redirect(response.notification); } const subscription = Notifications.addNotificationResponseReceivedListener(response => { redirect(response.notification); }); return () => { subscription.remove(); }; }, []); } export default function Layout() { useNotificationObserver(); return <Slot />; }

Configuration

Credentials

Follow the setup guide.

App config

To configure expo-notifications, use the built-in config plugin in the app config (app.json or app.config.js) for EAS Build 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:

Configurable properties

NameDefaultDescription
icon-
Only for: 
Android

Local path to an image to use as the icon for push notifications. 96x96 all-white png with transparency.

color#ffffff
Only for: 
Android

Tint color for the push notification image when it appears in the notification tray.

defaultChannel-
Only for: 
Android

Default channel for FCMv1 notifications.

sounds-

Array of local paths to sound files (.wav recommended) that can be used as custom notification sounds. Sound will not be played when focus mode does not permit it or silent mode is on.

enableBackgroundRemoteNotificationsfalse
Only for: 
iOS

Whether to enable background remote notifications, as described in Apple documentation. This updates the UIBackgroundModes key in the Info.plist to include remote-notification.

Here is an example of using the config plugin in the app config file:

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 } ] ] } }
Are you using this library in an existing React Native app?

Learn how to configure the native projects in the installation instructions in the expo-notifications repository.

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.

  • 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.

Android permissionDescription

RECEIVE_BOOT_COMPLETED

Allows an application to receive the Intent.ACTION_BOOT_COMPLETED that is broadcast after the system finishes booting.

SCHEDULE_EXACT_ALARM

Allows applications to use exact alarm APIs.

iOS

No usage description is required, see notification-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), and rare occasions when your notifications may be dropped.

Several listeners are exposed and documented in the Push notification behaviors section.

Headless (Background) notifications

See the definition of Headless Background Notifications in the 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:

Then send a push notification which:

Background notification configuration 
iOS

To be able to use headless (background) notifications 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, set the enableBackgroundRemoteNotifications property of the config plugin to true, and the correct configuration will be applied automatically by prebuild.

Configure UIBackgroundModes manually on iOS

If you're not using Continuous Native Generation (CNG) or you're using a native iOS project, then you'll need to add the following to your Expo.plist file:

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.

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 and NotificationChannelInput. You only need to provide the base filename. Here's an example using the config above:

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:

Manually adding notification sounds on Android

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/.

// 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 }, });
Manually adding notification sounds on iOS

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:

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.

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.

On iOS, notification categories also allow you to customize your notifications further. With each category, you can set the interactive actions a user can take and configure things like the placeholder text to display when the user disables notification previews for your app.

Platform-specific guides

Handling notification channels 
Android 8+

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 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.

Custom notification icon and colors 
Android

You can configure the icon and color keys for notification in the project by using the expo-notifications config plugin with Expo Prebuild. 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 (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 under the color attribute.

API

import * as Notifications from 'expo-notifications';

No API data file found, sorry!