๐Ÿ”” Comprehensive Guide to Implementing Push Notifications on Android using Firebase, FCM V1, and Expo in a Bare React Native App (2025 Edition)


If you're just starting out and want to integrate push notifications into your Android app using Firebase and Expo (bare workflow), this is your one-stop, no-step-skipped tutorial.


โœ… Prerequisites

npm install -g eas-cli

๐Ÿ“ฆ 1. Install Required Dependencies

Inside your project root:

npm install expo-notifications expo-device
npm install @react-native-async-storage/async-storage

Android-specific dependencies

Edit your android/build.gradle (Project-level):

buildscript {
  dependencies {
    classpath 'com.google.gms:google-services:4.3.15' // โœ… Add this line
  }
}

Edit your android/app/build.gradle (App-level):

dependencies {
  implementation 'com.google.firebase:firebase-messaging:23.0.8' // โœ… Add this
}

apply plugin: 'com.google.gms.google-services' // โœ… Add this at the bottom

๐Ÿ” 2. Set Up Firebase Project

  1. Go to Firebase Console

  2. Create a project (or use an existing one)

  3. Go to Project Settings > General

  4. Click โ€œAdd Appโ€ > Android

  5. Enter:

    • Android package name (e.g., com.jetriders)

    • App nickname: e.g., JetPush

    • Debug signing certificate (optional)

  6. Click โ€œRegister Appโ€


๐Ÿ“„ Download google-services.json

  1. After app registration, download the google-services.json file.

  2. Place it inside your project at:

android/app/google-services.json

โš™๏ธ 3. Set Up FCM V1 Credentials (Expo + EAS)

Step A: Generate a Firebase Admin SDK Key

  1. Go to Firebase Console > Project Settings > Service Accounts

  2. Click โ€œGenerate new private keyโ€

  3. Save the file as jetpushandroid-firebase-adminsdk-xyz123.json or similar

  4. Place it somewhere safe (e.g., ~/Downloads)

Step B: Upload it to EAS

eas credentials

Then:

  • Select platform: Android

  • Choose: Google Service Account

  • Then: Upload a Google Service Account Key

  • Enter the path to your .json file (e.g., /Users/yourname/Downloads/jetpushandroid-firebase-adminsdk-xyz123.json)

Then:

  • Choose Set up a Google Service Account Key for Push Notifications (FCM V1)

  • Select the key you just uploaded


๐Ÿ”‘ 4. Set Expo Project ID in registerForPushNotificationsAsync

import * as Notifications from 'expo-notifications';
import * as Device from 'expo-device';

export async function registerForPushNotificationsAsync() {
  let token;

  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!');
      return;
    }

    const tokenResponse = await Notifications.getExpoPushTokenAsync({
      projectId: 'your-expo-project-id-here', // โœ… Get this from `eas whoami` or your Expo dashboard
    });

    token = tokenResponse.data;
    console.log('๐Ÿ“ฆ Expo Push Token:', token);
  } else {
    alert('Must use physical device for Push Notifications');
  }

  return token;
}

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

๐Ÿง  5. Register for Push Notifications in App.js

import { registerForPushNotificationsAsync, setNotificationHandler } from './components/pushNotifications';
import AsyncStorage from '@react-native-async-storage/async-storage';

useEffect(() => {
  setNotificationHandler();

  registerForPushNotificationsAsync().then(async (token) => {
    if (token) {
      console.log('๐Ÿ”” Expo Push Token:', token);
      await AsyncStorage.setItem('expoPushToken', token);
    }
  });
}, []);

๐Ÿ“จ 6. Send Token to Your Backend

Inside your authenticated screen (e.g., DashboardScreen.js):

useEffect(() => {
  const sendPushTokenToBackend = async () => {
    const token = await AsyncStorage.getItem('expoPushToken');
    if (!token) return;

    await fetch('https://your-backend.com/save_push_token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email: user.email, token }),
    });
  };

  sendPushTokenToBackend();
}, []);

๐Ÿš€ 7. Sending a Test Notification from Terminal

After confirming that the token is stored:

curl -X POST https://exp.host/--/api/v2/push/send \
  -H "Content-Type: application/json" \
  -d '{
    "to": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]",
    "title": "JetPush ๐Ÿš€",
    "body": "With God, all things are possible ๐Ÿ™Œ",
    "sound": "default",
    "priority": "high"
  }'

๐Ÿ›  Final Notes

  • Use a real Android device (not an emulator).

  • Always use the FCM V1 method with your service account key via eas credentials.

  • Rebuild your app after changing any native config:

npx expo run:android

๐Ÿ™Œ Conclusion

This is the complete working process we used in our real JetRiders app to successfully implement push notifications on Android. We left no step behind, from Firebase setup, Gradle config, credentials, token registration, AsyncStorage, backend saving, to manual testing.

Youโ€™re ready to build and scale with confidence!


0
Subscribe to my newsletter

Read articles from Ogunuyo Ogheneruemu B directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ogunuyo Ogheneruemu B
Ogunuyo Ogheneruemu B

I'm Ogunuyo Ogheneruemu Brown, a senior software developer. I specialize in DApp apps, fintech solutions, nursing web apps, fitness platforms, and e-commerce systems. Throughout my career, I've delivered successful projects, showcasing strong technical skills and problem-solving abilities. I create secure and user-friendly fintech innovations. Outside work, I enjoy coding, swimming, and playing football. I'm an avid reader and fitness enthusiast. Music inspires me. I'm committed to continuous growth and creating impactful software solutions. Let's connect and collaborate to make a lasting impact in software development.