Skip to content

Set up push in React Native, Expo and Flutter

Use the Firebase messaging package your framework already has, register the token with SignOps, and send to Android and iOS from one call.

PushUpdated 28 October 20266 min read

React Native, Expo and Flutter apps use the Firebase or Expo package they already have to get a token. Your server adds that token to SignOps, and one call sends to Android and iOS. The SignOps SDK is not needed for push.

Before you start, do the account half of the native guides, which is the same for every framework:

  • Android: a Firebase project with your Android app, and its service account uploaded under Push, Setup, Android (Android guide, steps 1 and 5).
  • iOS: an APNs .p8 key (iOS guide, step 1), and the Push Notifications capability in Xcode (step 4).

Which token each setup gives

Framework and package Android token iOS token iOS route in SignOps
React Native with @react-native-firebase/messaging FCM FCM Through Firebase (upload the .p8 to Firebase)
Flutter with firebase_messaging FCM FCM Through Firebase (upload the .p8 to Firebase)
Expo with expo-notifications, getDevicePushTokenAsync FCM APNs Straight to Apple (upload the .p8 to SignOps)

Set the route under Push, Setup, iOS before your first iPhone registers.

React Native

npm install @react-native-firebase/app @react-native-firebase/messaging
cd ios && pod install

Put google-services.json in android/app/ and GoogleService-Info.plist in the iOS project (both from the Firebase console, one app each). Then, after sign-in:

import messaging from "@react-native-firebase/messaging";
import { PermissionsAndroid, Platform } from "react-native";

export async function turnOnPush() {
  if (Platform.OS === "android" && Platform.Version >= 33) {
    await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
  }
  await messaging().requestPermission();          // iOS asks here; Android returns at once
  const token = await messaging().getToken();
  await api.saveDevice({ token, platform: Platform.OS });   // "android" or "ios"
  messaging().onTokenRefresh((t) => api.saveDevice({ token: t, platform: Platform.OS }));
}

Expo

Expo push tokens (ExponentPushToken[...]) only work with Expo’s service. Ask for the phone’s own token instead:

import * as Notifications from "expo-notifications";
import { Platform } from "react-native";

export async function turnOnPush() {
  const { status } = await Notifications.requestPermissionsAsync();
  if (status !== "granted") return;
  const { data: token } = await Notifications.getDevicePushTokenAsync();   // FCM on Android, APNs on iOS
  await api.saveDevice({ token, platform: Platform.OS });
}

In app.json, set android.googleServicesFile to your google-services.json, then make a development build (npx expo prebuild, or EAS Build). Expo Go cannot use your own Firebase project. If pushes worked in development and stopped in the APK, see Expo push notifications not working. A runnable app and server: expo-push on GitHub.

Flutter

flutter pub add firebase_core firebase_messaging
dart pub global activate flutterfire_cli
flutterfire configure        # adds Android and iOS apps to your Firebase project

In Xcode (ios/Runner.xcworkspace), add the Push Notifications capability to the Runner target. Then, after sign-in:

import 'dart:io';
import 'package:firebase_messaging/firebase_messaging.dart';

Future<void> turnOnPush() async {
  final fm = FirebaseMessaging.instance;
  await fm.requestPermission();                       // iOS and Android 13 and later
  final token = await fm.getToken();
  final platform = Platform.isIOS ? 'ios' : 'android';
  if (token != null) await api.saveDevice(token, platform);
  fm.onTokenRefresh.listen((t) => api.saveDevice(t, platform));
}

Your server, for all three

api.saveDevice is your own endpoint. It knows the signed-in user and adds the token:

// Node, with npm install @signops/node
import { SignOps } from "@signops/node";
const signops = new SignOps(process.env.SIGNOPS_KEY);

app.post("/api/devices", requireUser, async (req, res) => {
  const device = await signops.devices.add({ user_id: req.user.id, platform: req.body.platform, token: req.body.token });
  res.json({ id: device.id });   // keep it, to remove the device when the user signs out
});

// Later, from anywhere on your server
await signops.push.send({ user_id: "usr_8f21c4", title: "Your order is out for delivery", body: "Ravi is 10 minutes away." });

Then send a test from Push, Setup in the dashboard with the user id, or with the call above, and check Push, Messages for what Google and Apple said.

Using Approve or passkeys too

Those need the SignOps module in the app (@signops/react-native or the signops Flutter plugin), which reuses the Firebase token you already have. It is not on npm or pub.dev yet; see the Approve guide for what it does and the calls it makes.

Frequently asked questions

More questions? Email us and the answer goes on this page.

hello@signops.dev
Can I send Expo push tokens (ExponentPushToken) through SignOps?

No. Those tokens only work with Expo's own push service. Get the phone's native token with getDevicePushTokenAsync from expo-notifications instead, which gives an FCM token on Android and an APNs token on iOS.

Does push work in Expo Go?

Not for testing your own Firebase project and Apple key. Make a development build with npx expo prebuild or EAS Build, so the app carries your google-services.json and push capability.

Which iOS route should a React Native or Flutter app pick?

Through Firebase if the app gets its token from Firebase Messaging, which is what @react-native-firebase/messaging and firebase_messaging give you. Straight to Apple if it sends the raw APNs token, as Expo's getDevicePushTokenAsync does on iOS.

Send your first push today.

Free for 1,000 push devices, 100 approvals and 1,000 passkey sign-ins a month, and 500 email codes to try. No card until you pass a free amount.