All articles
Mobile DevelopmentSeptember 26, 2026 6 min read

Push Notifications in React Native 2026: Expo Notifications vs FCM vs OneSignal in Production

We've shipped push on all three stacks — Expo Notifications, raw FCM/APNs, and OneSignal. Here's how they actually behave in production, including the token-refresh bugs nobody warns you about.

Push Notifications in React Native 2026: Expo Notifications vs FCM vs OneSignal in Production

Push is one of those features that looks like a checkbox on a spec doc and turns into a two-week detour once you start wiring it up. Every stack — Expo Notifications, raw FCM/APNs, OneSignal — claims to "just work." None of them do, not entirely. Here's what we've learned running push at scale across React Native apps in the last few years, and how we'd pick in 2026.

The three options, honestly framed

Before comparing, it helps to name what each thing actually is, because the marketing pages blur it.

  • Expo Notifications is a wrapper around APNs and FCM plus Expo's own push relay service. You send a request to exp.host, Expo forwards it to Apple or Google using credentials you uploaded. You get a unified ExpoPushToken regardless of platform.
  • FCM + APNs directly (via @react-native-firebase/messaging or the community notifee library) means you talk to Firebase Cloud Messaging for Android and either FCM-as-relay or APNs directly for iOS. You handle two tokens, two payload shapes, two sets of quirks.
  • OneSignal is a hosted notification platform. It sits on top of FCM and APNs, but adds segmentation, scheduling, A/B testing, in-app messages, and a dashboard your marketing team can actually use.

All three ultimately end up going through Apple and Google. The differences are in what you own, what you outsource, and what breaks at 2 a.m.

Setup cost and the first month of pain

Expo Notifications

If you're already on Expo and using EAS Build, this is genuinely the fastest path. You upload your APNs key and FCM service account JSON into your EAS project, install expo-notifications, and you're sending test pushes in an afternoon.

import * as Notifications from 'expo-notifications';

const { status } = await Notifications.requestPermissionsAsync();
if (status !== 'granted') return;

const token = (await Notifications.getExpoPushTokenAsync({
  projectId: Constants.expoConfig?.extra?.eas?.projectId,
})).data;

await fetch('https://your-api.example.com/register-push', {
  method: 'POST',
  body: JSON.stringify({ token }),
});

The catch: you're now dependent on Expo's push relay. It's been reliable in our experience, but it is a hop you don't own. If Expo has an incident, your pushes queue or drop until it recovers. For most B2C apps, this is a fine trade for the time saved. For a trading app or an on-call tool, it's not.

FCM + APNs directly

The most control, the most rope. Two SDK integrations, two credential rotations to manage, two sets of platform-specific payload keys (notification vs data vs aps, silent pushes, content-available, mutable-content, priority flags). You need to write your own sender service or use Firebase Admin SDK on your backend.

Budget a full sprint for the first integration. Budget another one the first time you need rich notifications, notification actions, or notification service extensions on iOS.

OneSignal

Sits somewhere in the middle for setup. The SDK is heavier than expo-notifications and has historically required config plugin gymnastics in Expo projects (their official config plugin has gotten better in the last couple of releases). Once installed, you get a dashboard, segmentation, and REST APIs that non-engineers can drive.

Token lifecycle: where everyone gets bitten

This is the part every push tutorial glosses over and every production team eventually rewrites.

Push tokens are not stable. They can rotate when:

  • The user reinstalls
  • The app is restored from an iCloud/Google backup to a new device
  • APNs decides to invalidate the token (yes, this happens)
  • The user disables and re-enables notifications
  • On Android, when Google Play Services is updated in certain ways

If your backend keeps a stale token, you'll silently stop delivering to that user and never know unless you check the delivery-receipt feedback.

What good token handling looks like

// Run on every cold start, after login, and after permission changes.
async function syncPushToken(userId: string) {
  const token = await getCurrentPushToken();
  const lastSynced = await AsyncStorage.getItem('push:lastToken');

  if (token !== lastSynced) {
    await api.registerToken({ userId, token, platform: Platform.OS });
    await AsyncStorage.setItem('push:lastToken', token);
  }
}

You also need to handle the reverse: when your send-service gets a Unregistered or InvalidRegistration response from FCM, or a 410 from APNs, delete that token from your database immediately. This is the single biggest reason engagement dashboards lie.

OneSignal handles this automatically — they maintain the token, mark subscriptions as unsubscribed on hard bounces, and expose a stable externalUserId you map to your own user IDs. That alone is worth the price for many teams.

Expo's push receipts API gives you similar data (DeviceNotRegistered errors), but you have to poll receipts within 24 hours and act on them yourself.

Raw FCM/APNs: you build all of this. It's not hard, it's just tedious, and it's the kind of code that gets written once and never revisited until it breaks.

Delivery reliability in the real world

All three routes end at the same two doorways: Apple and Google. In our experience, delivery rates are effectively identical once you're past the initial setup. What differs is latency and visibility.

  • Expo: adds a small hop through their relay. Fine for transactional or marketing pushes. We wouldn't build a real-time alerting product on it.
  • Direct FCM/APNs: lowest latency, especially with APNs HTTP/2 and priority 10 pushes. Best for time-critical alerts.
  • OneSignal: comparable latency to direct once tokens are warm; occasionally slower for very large broadcasts because they batch.

The visibility differences are bigger than the delivery differences. OneSignal's delivery dashboards and per-device event logs will save you hours of debugging. Building equivalent observability on top of raw FCM logs takes real work.

iOS-specific traps that apply to all three

Apple keeps tightening. A few things worth knowing before you ship:

  • Provisional authorization (UNAuthorizationOptionProvisional) lets you deliver quiet notifications without prompting, then let the user promote them. Underused, and it can meaningfully lift opt-in rates.
  • Focus modes and notification summaries mean your "delivered" push may not appear when you expect. There's no API to fight this; design around it.
  • Background/silent pushes (content-available: 1) are throttled aggressively. Don't build critical sync logic assuming they'll fire predictably. Use them as a hint, not a guarantee.
  • Live Activities and interactive notifications require a Notification Service Extension, which means a separate native target. Expo supports this via config plugins now, but you're still writing Swift.

Cost math

Rough shape as of 2026:

  • Expo Notifications: free for the push service itself. You pay for EAS if you use it for builds/updates, but the push relay has been free-with-fair-use.
  • FCM/APNs direct: free from Google and Apple. You pay for your own sender infra, which is trivial at most scales.
  • OneSignal: free tier is generous for subscriber counts up to a point, then tiered pricing that scales with monthly active subscribers and features. At a few hundred thousand MAUs with segmentation and journeys, it stops being cheap.

If marketing is going to run campaigns, the OneSignal (or Braze, Iterable, Customer.io) cost is easy to justify because it replaces engineering tickets. If push is purely transactional, paying a platform is hard to defend.

How we'd pick in 2026

A rough decision tree that's held up for us:

  • Solo dev or small team on Expo, transactional pushes only → Expo Notifications. Ship it, move on.
  • You need marketing-driven campaigns, segmentation, A/B tests → OneSignal (or a full CEP if budget allows). The dashboard pays for itself the first time PM ships a campaign without a deploy.
  • Latency-critical, regulated, or you need every ounce of control → Direct FCM + APNs via @react-native-firebase/messaging and notifee. Own the pipe.
  • Already deep in Firebase for auth, Firestore, analytics → Direct FCM. You're paying the Firebase tax anyway; use the tools.

Where we'd start

If you're greenfield on Expo in 2026, start with Expo Notifications and get the token lifecycle right on day one — cold-start sync, permission-change handling, and receipt polling with automatic cleanup of dead tokens. Ninety percent of "our push is broken" tickets are actually stale-token tickets. Get that layer solid, and swapping the transport later (to OneSignal, or to direct FCM) becomes a weekend of work instead of a rewrite.

If you want a hand designing a push architecture that survives a marketing team and an on-call rotation, our mobile team has done this on more apps than we'd care to count.

#React Native#Expo#Push Notifications#Mobile

Want a team like ours?

72Technologies builds production software for the kind of teams who actually read this blog.

Start a project