Setting Up Firebase Cloud Messaging V1 in React Native (Expo)

A beginner-friendly guide to sending push notifications — including image notifications — using Firebase Cloud Messaging HTTP V1 API with Expo.

Push Notifications

What You'll Learn

  • What FCM V1 is and why it replaced the legacy API
  • How to create a Firebase project and connect it to your app
  • How to collect both Expo and FCM tokens from the device
  • How to send text notifications via Expo and image notifications via FCM V1
  • How to test everything without writing backend code

Prerequisites

Before starting, make sure you have:

  • A React Native project using Expo (managed workflow)
  • Node.js installed
  • An Android device or emulator for testing
  • A Firebase account (free) at console.firebase.google.com

Part 1 — Understanding FCM V1

What is FCM?

Firebase Cloud Messaging (FCM) is Google's free service for sending push notifications to Android, iOS, and web apps. Your backend server sends a message to FCM, and FCM delivers it to the user's device.

Why V1 instead of Legacy?

Google deprecated the legacy FCM API in June 2024. The new HTTP V1 API is more secure and more powerful.

Feature Legacy API V1 API
Authentication Static Server Key OAuth 2.0 token (expires in 1hr)
Security Key can be leaked Short-lived tokens, harder to misuse
Image support Limited Full support
Per-platform config Basic Full Android / iOS / Web control
Status ❌ Deprecated ✅ Current standard

The Hybrid Approach (Recommended)

Expo's push service is great for simple text notifications but strips image fields from the payload. So the best setup is:

Text notification → Your Backend → Expo Push API → Device

Image notification → Your Backend → FCM V1 API directly → Device

Your app registers two tokens — one Expo token and one FCM device token — and your backend picks the right channel based on whether the notification has an image.

Part 2 — Firebase Project Setup

Step 1 — Create a Firebase Project

  1. Go to console.firebase.google.com
  2. Click Add project
  3. Enter a project name (e.g. MyApp)
  4. Disable Google Analytics if you don't need it
  5. Click Create project

Step 2 — Add Your Android App

  1. In the Firebase console, click the Android icon to add an app
  2. Enter your app's package name — find it in app.json
  3. Click Register app
  4. Download the google-services.json file
  5. Place it at the root of your project (same folder as app.json)

iOS users: Repeat with the iOS icon, download GoogleService-Info.plist, and place it at the project root too.

Step 3 — Get Your Service Account Key

You need this to generate OAuth tokens for the V1 API.

  1. In Firebase Console → Project Settings (gear icon)
  2. Click the Service accounts tab
  3. Click Generate new private key
  4. Download the JSON file — keep it safe, never commit it to git

Part 3 — App Setup

Step 4 — Install Dependencies

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

Step 5 — Update app.json

Add the google-services.json path and configure the notifications plugin:

{
  "expo": {
    "name": "MyApp",
    "android": {
      "package": "com.yourcompany.yourapp",
      "googleServicesFile": "./google-services.json"
    },
    "plugins": [
      [
        "expo-notifications",
        {
          "icon": "./assets/icon.png",
          "color": "#000000",
          "defaultChannel": "default"
        }
      ]
    ]
  }
}

Important: The google-services.json is a native file. Expo Go will not pick it up. You must build the app with EAS or expo run:android.

Step 6 — Create NotificationService.js

This file handles permission requests and collects both tokens. The code registers for push notifications, creates an Android notification channel, requests permissions, and retrieves both the Expo push token and FCM device token.

Part 4 — Building the App

Because google-services.json is a native file, you cannot test FCM with Expo Go. You need a real build.

Option A — EAS Build (recommended)

# Install EAS CLI if you haven't
npm install -g eas-cli

# Log in to your Expo account
eas login

# Build for Android
npx eas build --platform android --profile development

Install the resulting APK on your device.

Option B — Local Build

npx expo run:android

This requires Android Studio and the Android SDK to be set up on your machine.

Part 5 — Sending Notifications

How the Backend Decides Which API to Use

# Python pseudocode

def send_notification(user, title, body, image_url=None):
    if image_url:
        # Use FCM V1 directly — supports images
        send_via_fcm_v1(
            fcm_token=user.fcm_token,
            title=title,
            body=body,
            image=image_url
        )
    else:
        # Use Expo — simpler for text-only
        send_via_expo(
            expo_token=user.expo_token,
            title=title,
            body=body
        )

Sending via Expo Push API (text notifications)

// Node.js example
const response = await fetch('https://exp.host/--/api/v2/push/send', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    to: 'ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]',
    title: 'Hello!',
    body: 'This is a text notification.',
  }),
});

Sending via FCM V1 API (image notifications)

First, generate an OAuth access token from your service account. Then send the FCM V1 request with the notification payload including image URLs for different platforms.

Image Requirements:

  • Must be a public HTTPS URL (no redirects, no auth)
  • Recommended size: 1024 × 512px (2:1 ratio)
  • Format: JPG or PNG
  • Max size: ~1MB (Android downloads it at delivery time)

Part 6 — Testing Without a Backend

You can test FCM V1 directly from a browser using the service account JSON to auto-generate OAuth tokens. Use an FCM test tool to generate tokens, paste your FCM device token, fill in notification details, and send test notifications.

Common Errors and Fixes

INVALID_ARGUMENT — Invalid registration token

The FCM device token is wrong or expired. Get a fresh one from the app.

401 Unauthorized

Your OAuth access token has expired (they last ~1 hour). Generate a new one.

SENDER_ID_MISMATCH

The google-services.json in your app doesn't match the Firebase project you're sending from. Make sure both are from the same project.

Image not showing on device

Check these:

  • Image URL is HTTPS (not HTTP)
  • URL is publicly accessible — open it in a browser to verify
  • Image is under 1MB
  • You included the image field in both notification and android.notification
  • App was built with google-services.json included (not Expo Go)

Security Best Practices

Rule Why
Never commit service-account.json to git It gives full access to your Firebase project
Add it to .gitignore Prevents accidental commits
Never expose it in client-side code Anyone can use it to send notifications
Rotate the key if it leaks Firebase Console → Service Accounts → delete and regenerate
Use environment variables on your server process.env.FIREBASE_KEY instead of hardcoded paths

Summary

You've now set up a complete push notification system using Firebase Cloud Messaging V1 API with Expo. Your app collects both Expo and FCM tokens, and your backend can send text notifications via Expo's API and image notifications via FCM V1 directly.

Quick Checklist

  • ✓ Firebase project created
  • ✓ Android app added, google-services.json downloaded and placed at project root
  • app.json updated with googleServicesFile and notifications plugin
  • ✓ NotificationService.js collects both Expo and FCM tokens
  • ✓ Both tokens saved to your backend on app launch
  • ✓ App rebuilt with EAS or expo run:android (not Expo Go)
  • ✓ Backend sends text via Expo API, images via FCM V1 API
  • ✓ Image URLs are public HTTPS, 2:1 ratio, under 1MB

Written based on a real Expo + Firebase V1 setup. Last updated: May 2026.