> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/expo/expo/llms.txt
> Use this file to discover all available pages before exploring further.

# Push Notifications

> Learn how to implement push notifications in your Expo app with Firebase Cloud Messaging and Apple Push Notification service

## Overview

Push notifications allow you to engage users even when your app isn't running. Expo provides `expo-notifications` to handle both local and remote notifications across iOS and Android.

## Installation

<Steps>
  <Step title="Install the package">
    ```bash theme={null}
    npx expo install expo-notifications expo-device expo-constants
    ```
  </Step>

  <Step title="Configure your app.json">
    Add notification configuration to your `app.json`:

    ```json app.json theme={null}
    {
      "expo": {
        "plugins": [
          [
            "expo-notifications",
            {
              "icon": "./assets/notification-icon.png",
              "color": "#ffffff",
              "sounds": ["./assets/notification-sound.wav"]
            }
          ]
        ],
        "android": {
          "googleServicesFile": "./google-services.json"
        },
        "ios": {
          "infoPlist": {
            "UIBackgroundModes": ["remote-notification"]
          }
        }
      }
    }
    ```
  </Step>

  <Step title="Request permissions">
    Create a hook to manage notification permissions:

    ```typescript hooks/useNotifications.ts theme={null}
    import { useState, useEffect, useRef } from 'react';
    import * as Notifications from 'expo-notifications';
    import * as Device from 'expo-device';
    import { Platform } from 'react-native';

    export function useNotifications() {
      const [expoPushToken, setExpoPushToken] = useState<string>();
      const [notification, setNotification] = useState<Notifications.Notification>();
      const notificationListener = useRef<Notifications.Subscription>();
      const responseListener = useRef<Notifications.Subscription>();

      useEffect(() => {
        registerForPushNotificationsAsync().then(token => {
          setExpoPushToken(token);
        });

        notificationListener.current = Notifications.addNotificationReceivedListener(notification => {
          setNotification(notification);
        });

        responseListener.current = Notifications.addNotificationResponseReceivedListener(response => {
          console.log('User tapped notification:', response);
        });

        return () => {
          notificationListener.current?.remove();
          responseListener.current?.remove();
        };
      }, []);

      return { expoPushToken, notification };
    }

    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;
        }
        
        token = (await Notifications.getExpoPushTokenAsync()).data;
      } else {
        alert('Must use physical device for Push Notifications');
      }

      return token;
    }
    ```
  </Step>
</Steps>

## Handling Notifications

### Configure notification behavior

Set how notifications are displayed when the app is foregrounded:

```typescript App.tsx theme={null}
import * as Notifications from 'expo-notifications';

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

### Listen for notifications

<Tabs>
  <Tab title="Foreground">
    ```typescript theme={null}
    useEffect(() => {
      const subscription = Notifications.addNotificationReceivedListener(notification => {
        console.log('Notification received:', notification);
        // Update UI, show in-app notification, etc.
      });

      return () => subscription.remove();
    }, []);
    ```
  </Tab>

  <Tab title="Background/Quit">
    ```typescript theme={null}
    useEffect(() => {
      const subscription = Notifications.addNotificationResponseReceivedListener(response => {
        const { notification } = response;
        const data = notification.request.content.data;
        
        // Navigate based on notification data
        if (data.screen) {
          router.push(data.screen);
        }
      });

      return () => subscription.remove();
    }, []);
    ```
  </Tab>
</Tabs>

## Sending Notifications

### Local notifications

```typescript theme={null}
async function scheduleLocalNotification() {
  await Notifications.scheduleNotificationAsync({
    content: {
      title: "Time's up!",
      body: 'Your timer has finished',
      data: { screen: '/timer' },
      sound: 'notification-sound.wav',
    },
    trigger: {
      seconds: 60,
      // Or use a specific date:
      // date: new Date(Date.now() + 60 * 1000)
    },
  });
}
```

### Push notifications from your server

<Tabs>
  <Tab title="Node.js">
    ```javascript theme={null}
    const sendPushNotification = async (expoPushToken, title, body, data) => {
      const message = {
        to: expoPushToken,
        sound: 'default',
        title,
        body,
        data,
        priority: 'high',
        channelId: 'default',
      };

      await fetch('https://exp.host/--/api/v2/push/send', {
        method: 'POST',
        headers: {
          Accept: 'application/json',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(message),
      });
    };
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import requests
    import json

    def send_push_notification(expo_push_token, title, body, data=None):
        message = {
            'to': expo_push_token,
            'sound': 'default',
            'title': title,
            'body': body,
            'data': data or {},
            'priority': 'high',
            'channelId': 'default',
        }

        response = requests.post(
            'https://exp.host/--/api/v2/push/send',
            headers={
                'Accept': 'application/json',
                'Content-Type': 'application/json',
            },
            data=json.dumps(message)
        )
        return response.json()
    ```
  </Tab>
</Tabs>

## Platform-Specific Setup

<Tabs>
  <Tab title="iOS - APNs">
    ### Apple Push Notification Service

    <Steps>
      <Step title="Create an APNs key">
        1. Go to [Apple Developer Portal](https://developer.apple.com/account/resources/authkeys/list)
        2. Create a new key with Push Notifications enabled
        3. Download the `.p8` file
      </Step>

      <Step title="Upload to Expo">
        ```bash theme={null}
        eas credentials
        ```

        Select your iOS app and upload the APNs key.
      </Step>

      <Step title="Enable push notifications capability">
        In your `app.json`:

        ```json theme={null}
        {
          "expo": {
            "ios": {
              "entitlements": {
                "aps-environment": "production"
              }
            }
          }
        }
        ```
      </Step>
    </Steps>
  </Tab>

  <Tab title="Android - FCM">
    ### Firebase Cloud Messaging

    <Steps>
      <Step title="Create a Firebase project">
        1. Go to [Firebase Console](https://console.firebase.google.com/)
        2. Create a new project or select an existing one
        3. Add an Android app with your package name
      </Step>

      <Step title="Download google-services.json">
        Download the `google-services.json` file and place it in your project root.
      </Step>

      <Step title="Upload FCM server key to Expo">
        ```bash theme={null}
        eas credentials
        ```

        Select your Android app and upload the FCM server key (found in Firebase Project Settings > Cloud Messaging).
      </Step>
    </Steps>
  </Tab>
</Tabs>

## Notification Channels (Android)

Android 8.0+ requires notification channels:

```typescript theme={null}
if (Platform.OS === 'android') {
  await Notifications.setNotificationChannelAsync('messages', {
    name: 'Messages',
    importance: Notifications.AndroidImportance.HIGH,
    vibrationPattern: [0, 250, 250, 250],
    sound: 'message-sound.wav',
    lightColor: '#FF231F7C',
    lockscreenVisibility: Notifications.AndroidNotificationVisibility.PUBLIC,
    bypassDnd: false,
  });

  await Notifications.setNotificationChannelAsync('alerts', {
    name: 'Alerts',
    importance: Notifications.AndroidImportance.MAX,
    vibrationPattern: [0, 500, 500, 500],
    sound: 'alert-sound.wav',
  });
}
```

## Testing Notifications

### Test with Expo push tool

Use the [Expo Push Notification Tool](https://expo.dev/notifications) to send test notifications:

1. Get your Expo push token from your app
2. Enter it in the tool
3. Compose and send a test notification

### Test locally

```typescript theme={null}
import * as Notifications from 'expo-notifications';

// In your component
const sendTestNotification = async () => {
  await Notifications.scheduleNotificationAsync({
    content: {
      title: 'Test Notification',
      body: 'This is a test',
      data: { testData: 'test' },
    },
    trigger: null, // Send immediately
  });
};
```

## Troubleshooting

<AccordionGroup>
  <Accordion title="Notifications not received on iOS">
    * Ensure you're testing on a physical device (not simulator)
    * Check that APNs credentials are correctly configured
    * Verify push notifications capability is enabled
    * Check that your app is properly signed
  </Accordion>

  <Accordion title="Notifications not showing when app is foregrounded">
    Make sure you've set up the notification handler:

    ```typescript theme={null}
    Notifications.setNotificationHandler({
      handleNotification: async () => ({
        shouldShowAlert: true,
        shouldPlaySound: true,
        shouldSetBadge: true,
      }),
    });
    ```
  </Accordion>

  <Accordion title="Push token not generating">
    * Verify you're using a physical device
    * Check that all permissions are granted
    * For iOS: Ensure APNs is properly configured
    * For Android: Verify google-services.json is present
  </Accordion>

  <Accordion title="Sound not playing">
    * Ensure the sound file is in the correct format (WAV or MP3)
    * Add the sound file to your assets and reference it in app.json
    * For Android, set up the notification channel with the sound
  </Accordion>
</AccordionGroup>

<Warning>
  Push notifications require physical devices for testing. The iOS simulator and some Android emulators don't support push notifications.
</Warning>

## Best Practices

* **Request permission contextually**: Ask for notification permissions when the user takes an action that benefits from notifications
* **Use notification channels**: Create separate channels for different types of notifications on Android
* **Include deep links**: Add data to notifications to navigate users to relevant content
* **Handle notification lifecycle**: Listen for notifications in all app states (foreground, background, quit)
* **Test thoroughly**: Test on both iOS and Android physical devices
* **Store tokens securely**: Send push tokens to your backend and associate them with user accounts
* **Handle token refresh**: Listen for token updates and update your backend
* **Badge management**: Clear badges when appropriate using `Notifications.setBadgeCountAsync(0)`

## Related Resources

* [expo-notifications documentation](https://docs.expo.dev/versions/latest/sdk/notifications/)
* [Expo Push Notification Tool](https://expo.dev/notifications)
* [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging)
* [Apple Push Notification Service](https://developer.apple.com/documentation/usernotifications)
