Skip to main content

expo-notifications

Version: 55.0.7 Provides an API to fetch push notification tokens and to present, schedule, receive, and respond to notifications. Supports both local and remote push notifications.

Installation

npx expo install expo-notifications

Usage

import * as Notifications from 'expo-notifications';

// Configure notification behavior
Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true,
    shouldPlaySound: true,
    shouldSetBadge: false,
  }),
});

// Request permissions
const { status } = await Notifications.requestPermissionsAsync();

// Get push token
const token = (await Notifications.getExpoPushTokenAsync()).data;

// Schedule local notification
await Notifications.scheduleNotificationAsync({
  content: {
    title: 'Hello!',
    body: 'This is a notification',
  },
  trigger: {
    seconds: 5,
  },
});

// Listen for notifications
Notifications.addNotificationReceivedListener((notification) => {
  console.log('Notification received:', notification);
});

Notifications.addNotificationResponseReceivedListener((response) => {
  console.log('Notification tapped:', response);
});

API Reference

Permissions

requestPermissionsAsync(request)
(request?: NotificationPermissionsRequest) => Promise<NotificationPermissionsStatus>
Requests notification permissions
const { status } = await Notifications.requestPermissionsAsync({
  ios: {
    allowAlert: true,
    allowBadge: true,
    allowSound: true,
  },
});
getPermissionsAsync()
() => Promise<NotificationPermissionsStatus>
Gets current permission status

Push Tokens

getExpoPushTokenAsync(options)
(options?: ExpoPushTokenOptions) => Promise<ExpoPushToken>
Gets Expo push token
const token = await Notifications.getExpoPushTokenAsync({
  projectId: 'your-project-id',
});
console.log('Token:', token.data);
getDevicePushTokenAsync()
() => Promise<DevicePushToken>
Gets native device push token (FCM/APNs)

Local Notifications

scheduleNotificationAsync(request)
(request: NotificationRequestInput) => Promise<string>
Schedules a local notification
const id = await Notifications.scheduleNotificationAsync({
  content: {
    title: 'Reminder',
    body: 'Don\'t forget!',
    data: { customData: 'value' },
  },
  trigger: {
    seconds: 10,
  },
});
presentNotificationAsync(content)
(content: NotificationContentInput) => Promise<string>
Shows notification immediately
await Notifications.presentNotificationAsync({
  title: 'Now',
  body: 'Immediate notification',
});
cancelScheduledNotificationAsync(identifier)
(identifier: string) => Promise<void>
Cancels scheduled notification
cancelAllScheduledNotificationsAsync()
() => Promise<void>
Cancels all scheduled notifications
getAllScheduledNotificationsAsync()
() => Promise<Notification[]>
Gets all scheduled notifications

Notification Management

dismissNotificationAsync(identifier)
(identifier: string) => Promise<void>
Dismisses displayed notification
dismissAllNotificationsAsync()
() => Promise<void>
Dismisses all displayed notifications
getPresentedNotificationsAsync()
() => Promise<Notification[]>
Gets currently displayed notifications

Badge Count

setBadgeCountAsync(count)
(count: number) => Promise<boolean>
Sets app badge count
await Notifications.setBadgeCountAsync(5);
getBadgeCountAsync()
() => Promise<number>
Gets current badge count

Event Listeners

addNotificationReceivedListener(listener)
(listener: (notification: Notification) => void) => Subscription
Listens for received notifications
const subscription = Notifications.addNotificationReceivedListener(
  (notification) => {
    console.log('Received:', notification.request.content.title);
  }
);
addNotificationResponseReceivedListener(listener)
(listener: (response: NotificationResponse) => void) => Subscription
Listens for notification taps
Notifications.addNotificationResponseReceivedListener((response) => {
  const data = response.notification.request.content.data;
  // Navigate based on data
});

Configuration

setNotificationHandler(handler)
(handler: NotificationHandler | null) => void
Configures notification behavior
Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true,
    shouldPlaySound: true,
    shouldSetBadge: true,
    priority: Notifications.AndroidNotificationPriority.HIGH,
  }),
});
setNotificationChannelAsync(identifier, channel)
(identifier: string, channel: NotificationChannel) => Promise<NotificationChannel | null>
Creates/updates Android notification channel
await Notifications.setNotificationChannelAsync('default', {
  name: 'Default',
  importance: Notifications.AndroidImportance.HIGH,
  vibrationPattern: [0, 250, 250, 250],
  sound: 'notification.wav',
});

Examples

Complete Setup

import * as Notifications from 'expo-notifications';
import { useState, useEffect, useRef } from 'react';
import { Platform } from 'react-native';

// Configure handler
Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true,
    shouldPlaySound: true,
    shouldSetBadge: false,
  }),
});

export default function App() {
  const [pushToken, setPushToken] = useState('');
  const notificationListener = useRef<Notifications.Subscription>();
  const responseListener = useRef<Notifications.Subscription>();

  useEffect(() => {
    registerForPushNotifications();

    // Add listeners
    notificationListener.current = Notifications.addNotificationReceivedListener(
      (notification) => {
        console.log('Notification received:', notification);
      }
    );

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

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

  async function registerForPushNotifications() {
    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');
      return;
    }

    const token = (await Notifications.getExpoPushTokenAsync()).data;
    setPushToken(token);

    // Send token to your server
    await sendTokenToServer(token);

    // Android channel
    if (Platform.OS === 'android') {
      Notifications.setNotificationChannelAsync('default', {
        name: 'default',
        importance: Notifications.AndroidImportance.MAX,
      });
    }
  }

  // Rest of component...
}

Schedule Notification

import * as Notifications from 'expo-notifications';

async function scheduleReminder() {
  await Notifications.scheduleNotificationAsync({
    content: {
      title: 'Reminder',
      body: 'Time to check your tasks',
      data: { screen: 'tasks' },
    },
    trigger: {
      hour: 9,
      minute: 0,
      repeats: true,
    },
  });
}

Handle Notification Tap

import * as Notifications from 'expo-notifications';
import { useNavigation } from '@react-navigation/native';

function useNotifications() {
  const navigation = useNavigation();

  useEffect(() => {
    const subscription = Notifications.addNotificationResponseReceivedListener(
      (response) => {
        const { screen } = response.notification.request.content.data;
        
        if (screen) {
          navigation.navigate(screen as never);
        }
      }
    );

    return () => subscription.remove();
  }, []);
}

Platform Support

PlatformSupported
iOS
Android
Web✅ (Limited)

Configuration

app.json

{
  "expo": {
    "plugins": [
      [
        "expo-notifications",
        {
          "icon": "./assets/notification-icon.png",
          "color": "#ffffff",
          "sounds": ["./assets/notification.wav"]
        }
      ]
    ],
    "notification": {
      "icon": "./assets/notification-icon.png",
      "color": "#ffffff"
    }
  }
}

Permissions

iOS: Add to app.json:
{
  "ios": {
    "infoPlist": {
      "UIBackgroundModes": ["remote-notification"]
    }
  }
}
Android: Permissions automatically added.

Best Practices

  1. Request Permissions: Always request before sending notifications
  2. Configure Handler: Set notification handler before listeners
  3. Clean Up: Remove listeners in component cleanup
  4. Android Channels: Create channels for Android 8+
  5. Error Handling: Handle permission denials gracefully
On iOS, notifications may not appear when app is in foreground unless configured in notification handler.

Resources