React Native course ยท Module 7: Notifications

Push Notifications

2 min read ยท
In this lesson4

Push notifications are sent from a server to the user's device.

Expo Push Service

// On the server (Node.js)
import { Expo, ExpoPushMessage } from 'expo-server-sdk';

const expo = new Expo();

async function sendPushNotification(pushToken: string, title: string, body: string) {
  const message: ExpoPushMessage = {
    to: pushToken,
    sound: 'default',
    title,
    body,
    data: { someData: 'goes here' },
  };

  const chunks = expo.chunkPushNotifications([message]);
  const tickets = [];

  for (const chunk of chunks) {
    const ticketChunk = await expo.sendPushNotificationsAsync(chunk);
    tickets.push(...ticketChunk);
  }

  return tickets;
}

Sending with curl (testing)

curl -H "Content-Type: application/json" -X POST \
  "https://exp.host/--/api/v2/push/send" \
  -d '{
    "to": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]",
    "title":"Message from Atlantis",
    "body": "Poseidon summons you!"
  }'

Storing Token on Server

// In the app - after user registration
async function savePushToken() {
  const token = await registerForPushNotificationsAsync();

  if (token) {
    // Send token to your server
    await fetch('https://api.atlantis.app/users/push-token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        token: token.data,
        platform: Platform.OS,
      }),
    });
  }
}

Handling Received Notifications

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

function useNotifications() {
  const notificationListener = useRef<Notifications.Subscription>();
  const responseListener = useRef<Notifications.Subscription>();

  useEffect(() => {
    // Listener for received notification (app in foreground)
    notificationListener.current = Notifications.addNotificationReceivedListener(
      (notification) => {
        console.log('Received:', notification);
      }
    );

    // Listener for notification interaction
    responseListener.current = Notifications.addNotificationResponseReceivedListener(
      (response) => {
        const data = response.notification.request.content.data;
        console.log('Notification tapped:', data);

        // Navigate based on data
        if (data.screen) {
          navigation.navigate(data.screen, data);
        }
      }
    );

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

Push notifications allow communication with the user even when the app is closed!

Code for this lesson: App.tsx
import { useEffect, useRef, useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import * as Notifications from 'expo-notifications';

// Hook handling notification receipt and tap
function useNotifications() {
  const [lastNotification, setLastNotification] = useState<string>('');
  const notificationListener = useRef<Notifications.Subscription>();
  const responseListener = useRef<Notifications.Subscription>();

  useEffect(() => {
    // TODO: listener for a received notification (app in foreground)
    // use Notifications.addNotificationReceivedListener
    notificationListener.current = Notifications.addNotificationReceivedListener(
      (notification) => {
        // TODO: save the notification title into state
        setLastNotification('');
      }
    );

    // TODO: listener for tapping/interacting with the notification
    // use Notifications.addNotificationResponseReceivedListener
    responseListener.current = Notifications.addNotificationResponseReceivedListener(
      (response) => {
        const data = response.notification.request.content.data;
        console.log('Tapped:', data);
      }
    );

    // TODO: clean up subscriptions in the cleanup function (remove())
    return () => {
      notificationListener.current?.remove();
      responseListener.current?.remove();
    };
  }, []);

  return { lastNotification };
}

export default function App() {
  const { lastNotification } = useNotifications();

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Push Notifications</Text>
      <Text style={styles.info}>Last: {lastNotification || 'none'}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#1a5276' },
  title: { fontSize: 24, color: '#fff', marginBottom: 12 },
  info: { fontSize: 14, color: '#aed6f1' },
});

Docs entries used in this lesson

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. What format does the Expo Push Token have?

  2. 2. To which URL do we send push via Expo?

These are 2 of 4 questions for this lesson. Solve the rest in the game.

Hands-on tasks in the game

  • Vertical ordering

    Arrange the push notification payload:

  • Code editor

    Complete the code with listeners.

Useful articles