๐ 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
Node.js and npm installed
A working React Native bare workflow app (not using Expo Go)
Firebase project created at https://console.firebase.google.com
EAS CLI installed:
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
Go to Firebase Console
Create a project (or use an existing one)
Go to Project Settings > General
Click โAdd Appโ > Android
Enter:
Android package name (e.g.,
com.jetriders
)App nickname: e.g., JetPush
Debug signing certificate (optional)
Click โRegister Appโ
๐ Download google-services.json
After app registration, download the
google-services.json
file.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
Go to Firebase Console > Project Settings > Service Accounts
Click โGenerate new private keyโ
Save the file as
jetpushandroid-firebase-adminsdk-xyz123.json
or similarPlace 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!
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.