logo

Expo Push Notifications and Firebase Cloud Messaging (FCM V1) on Android: Complete Setup Guide

Push notifications are a crucial way for mobile apps to engage users, delivering timely updates, alerts, and personalized content straight to their devices. In the React Native ecosystem, Expo makes it easy to implement push notifications without diving into complex native setup. By integrating Expo with Firebase Cloud Messaging (FCM), developers can receive notifications reliably across platforms and test them on real devices. In this article, we'll focus on configuring Expo push notifications with FCM, using a MacBook for development and an Android device for testing. We'll also demonstrate how to send notifications through popular push notification tools to ensure that your setup is working correctly.

What Are Expo Push Notifications vs Firebase Cloud Messaging (FCM)?

Expo provides a push notification tool that allows developers to send notifications to Android and iOS devices without dealing with all the underlying infrastructure. On Android, however, this tool relies on Firebase Cloud Messaging (FCM V1) to actually deliver the messages. FCM is Google's native push notification service, responsible for message routing and delivery. While most Expo developers use the Expo push notification tool for its ease of use and cross-platform support, you can also send messages directly through Firebase if you need more control, such as sending custom payloads or testing notifications directly from the Firebase console. In short, Expo's tool acts as a convenient interface, while FCM is the engine that ensures messages reliably reach devices.

Historically, Expo Push Notifications relied on the Firebase Cloud Messaging (FCM) Legacy API, which only required a simple server key. Because of this, push notifications sometimes worked even with minimal Firebase configuration. However, Google deprecated the legacy protocol and introduced the FCM HTTP v1 API, which requires OAuth authentication using a Firebase service account. After Expo migrated to this new API, a proper Firebase setup (including a service account key and project configuration) became mandatory for Android push notifications.

Prerequisites

Before setting up Expo push notifications with Firebase Cloud Messaging, make sure your development environment is ready:

MacBook Development Tools:
✓ Node.js (v18+)
✓ Expo CLI (npm install -g expo-cli)
✓ Java JDK 11+ (brew install openjdk@17)
✓Expo project

Android Tools:
✓ Android SDK (cmdline-tools)
✓ ADB (platform-tools)
✓ Physical Android device with developer mode enabled

✓✓ Hardware: Android device and a USB Type-C cable* for connecting the device to your MacBook.
*If you prefer not to connect your Android device using a USB cable, you can also test the app by installing the APK file directly on the device. After building the APK, transfer it to your phone (for example via email, cloud storage, or messaging apps) and install it manually. Make sure that installation from unknown sources is enabled on your Android device.

Author's Note
I'll be honest: I didn't love the "new" Expo experience. Setting up push notifications used to feel like magic; now, it feels like a test of patience.
If you're coming from the old way of doing things, prepare for a few shocks:
The Build Queue: I spent three hours waiting for an EAS build. In a world of "instant" development, this felt like a massive step backward.
The FCM Overkill: Having to manually configure the entire Firebase (FCM) environment just to get an Expo push token felt unnecessary and tedious.
The Death of Expo Go: You can't use Expo Go for this anymore. You must have a physical device and you must build a custom Development Build.
The process is no longer straightforward, and the "silent" download errors without clear explanations didn't help. I'm writing this guide to save you the hours I lost, so you can get your notifications running without the headache.

If you haven't set up your project yet, make sure to follow the setup and installation guide before continuing. This article was written using **Expo SDK 54.0.33 and **Expo Dev Client v6.0.20.

Before starting, make sure you have an Expo account. Then, create a Firebase project (follow the official Firebase guide) to set up Cloud Messaging for your app.

Install Dependencies

npx expo install expo-notifications expo-device expo-constants

How to get and set up Google Service Account Keys using FCM V1

Before proceeding, make sure eas-cli and expo-dev-client are installed (npm install -g eas-cli, npx expo install expo-dev-client).

Generate a Google Service Account Key for FCM V1

1. Go to the Firebase Console and create a new project.
2. Open Project Settings > Service Accounts.
3. Click Generate New Private Key and download the JSON file containing the private key. Store this file in your Expo project (you can drag it into the project folder after downloading).
4. In the Firebase Console, open Project Settings > General, then download the google-services.json file under Your Apps.

Add google-services.json to Your Expo Project

5. Place the google-services.json file in the root of your Expo project.

Configure Your Expo Project for EAS Builds

6. Run npx eas build:configure to create an eas.json file in your project root. When prompted, select Android.
7. Add the following configuration to your app.json:

"expo": {
    ...
       "android": {
          "package": "com.package_name.pushnotifications",
          "googleServicesFile": "./google-services.json"
       }
}

Upload a Google Service Account Key via Terminal (EAS Credentials for FCM V1)

8. Open your project's terminal and run:

eas credentials


9. Select Android > development > Google Service account
10. Select Manage your Google Service Account Key for Push Notifications (FCM V1)
11. Set up a Google Service Account Key for Push Notifications (FCM V1) > Upload a new service account key
12. If you've previously stored the JSON file in your project directory, the eas-cli automatically detects the file and prompts you to select it. Press Y to continue.
13. In your app.json, add "expo-notifications" under the plugins array to enable push notifications:

{
"expo": {
    ...
    "plugins": ["expo-notifications"]
  }
}

You can use the code below—or any equivalent implementation—to test both push notifications and Firebase Cloud Messaging. The example retrieves both the Expo push token and the Firebase Cloud Messaging device token, allowing notifications to be sent either through the Expo Push Notification service or directly through FCM. You can test each system separately if needed.

How to Get Expo Push Notification Tokens and Firebase Cloud Messaging Tokens


import * as Notifications from "expo-notifications";
import React, { useEffect, useState } from "react";
import { Platform, Text, View } from "react-native";

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

export default function App() {
  const [expoToken, setExpoToken] = useState("Fetching Expo token...");
  const [fcmToken, setFcmToken] = useState("Fetching FCM token...");

  useEffect(() => {
    async function registerForPushNotificationsAsync() {
      // Android channel
      if (Platform.OS === "android") {
        await Notifications.setNotificationChannelAsync("default", {
          name: "default",
          importance: Notifications.AndroidImportance.MAX,
          vibrationPattern: [0, 250, 250, 250],
          lightColor: "#FF231F7C",
        });
      }

      // Request permissions
      const { status: existingStatus } =
        await Notifications.getPermissionsAsync();
      let finalStatus = existingStatus;
      if (existingStatus !== "granted") {
        const { status } = await Notifications.requestPermissionsAsync();
        finalStatus = status;
      }

      if (finalStatus !== "granted") {
        setExpoToken("Permission not granted!");
        setFcmToken("Permission not granted!");
        return;
      }

      try {
        // Expo push token
        const expoResponse = await Notifications.getExpoPushTokenAsync();
        setExpoToken(expoResponse.data);

        // Native FCM token
        const fcmResponse = await Notifications.getDevicePushTokenAsync();
        setFcmToken(fcmResponse.data);

        console.log("Expo Push Token:", expoResponse.data);
        console.log("Native FCM Token:", fcmResponse.data);
      } catch (error) {
        setExpoToken("Error: " + error.message);
        setFcmToken("Error: " + error.message);
      }
    }

    registerForPushNotificationsAsync();
  }, []);

  return (
    <View
      style={{
        flex: 1,
        justifyContent: "center",
        alignItems: "center",
        padding: 20,
      }}>
      <Text style={{ fontWeight: "bold" }}>Expo Push Token:</Text>
      <Text
        selectable
        style={{ color: "blue", textAlign: "center", marginBottom: 20 }}>
        {expoToken}
      </Text>

      <Text style={{ fontWeight: "bold" }}>Native FCM Token:</Text>
      <Text
        selectable
        style={{ color: "green", textAlign: "center", marginTop: 10 }}>
        {fcmToken}
      </Text>
    </View>
  );
}


Open the terminal and log in to Expo using your email and password:

npx expo login

Configure EAS (if you have not already done so):

npx eas build:configure

Create a development build of your Expo app for Android:

eas build --platform android --profile development

eas stands for Expo Application Services. build tells EAS to compile your app into a native binary (APK or AAB) using Expo's cloud build system.

Plug in your android device. Enable Developer Mode and USB debugging on the device. Connect the device to your MacBook using a USB Type-C cable.

Enable Developer Options on the phone. Enable USB Debugging. Allow the computer when the phone asks for USB debugging authorization.

After connecting the device, run adb devices to list available Android devices. If your device appears in the list, it means ADB can communicate with it, and you can safely install the APK:

adb install path/to/your-app.apk

Install the APK on your device to download the app.

If you cannot transfer the APK file manually, follow these steps:

1. Go to your Expo account.
2. Select your project.
3. Open the builds section.
4. Select install.
5. Scan the QR code with your device.
6. Download the APK file to your device.

Finally, run the following command:

npx expo start --dev-client

How to send a notification using Expo Push Notifications Tool

1. Go to the Expo Push Notifications Tool
2. Enter the ExponentPushToken[XXXX] of the device in the Recipient field.
3. Fill in the Message Title and Message Body.
4. The other fields are optional.
5. Click Send Notification to test it on your device.

How to send a Firebase Cloud Message

1. Open the Firebase Console.
2. Open the Messaging section.
3. Select Create a new campaign.
4. Fill in the Notification Title & Notification Text.
5. Add the FCM Registration Token you obtained from your app code.
6. Test

Troubleshooting

⇒Stuck during configuration? The first thing to do is make sure all necessary packages are correctly installed:

java -version (Should be 17 or 21)
sdkmanager --list (Confirms Command Line Tools are mapped)
eas --version (Confirms EAS CLI is ready)

⇒Check that your google-services.json file is in the root of your project:

"android": {
    "googleServicesFile": "./google-services.json",
    "package": "com.yourname.projectname"
}

Sender ID / Project Number: Don't assume your Sender ID and Project Number are the same. Check your Firebase Project Settings under the Cloud Messaging tab and make sure the Sender ID listed there exactly matches the one in your google-services.json. If you've regenerated your Firebase project or switched environments, an old ID in your config file will break the notification handshake every time.

Package Name: Your app's package name must exactly match the one in the Firebase Console (for example, com.username.app).

Device Installation Issues: If you have trouble downloading or installing the app on your device, check whether another app with the same package name is already installed and uninstall it before installing the new build.

Stuck on the Terminal Setup?
Setting up Android Command Line Tools and Java Paths on a Mac can be a nightmare—especially if you're trying to avoid the Android Studio bloat. If you want a step-by-step breakdown of how I configured my environment to bypass the usual setup traps, just send me an email with the subject "Expo Terminal Guide." If there's enough interest, I'll publish a separate deep-dive on the minimalist environment setup.

Conclusion

Google replaced the FCM Legacy API with the newer HTTP v1 API to improve security and control. Instead of a static server key, the new system uses temporary OAuth tokens from a Firebase service account, which makes notifications safer and enables more advanced features. Setting up Expo push notifications with Firebase Cloud Messaging (FCM) may seem tricky at first, but once the configuration steps are properly followed, testing push notifications on an Android device becomes straightforward. In this article, we walked through the essential prerequisites, configuring your Expo project, integrating FCM, building a development APK, and testing notifications using push tools. By carefully following these steps, you can ensure your app reliably receives notifications and is ready for further development or production deployment.

Need to adjust your permission settings? See our tutorial on handling different permissions for step-by-step guidance.

To learn more about Expo, check out our detailed Expo tutorial.