Skip to main content
Tutorial

Push Notifications in Expo: The Complete Guide I Wish Existed When I Started

4 min read

A developer's practical guide to setting up Expo push notifications, with real-world lessons from Gulf region projects.

React NativeExpoPush NotificationsFirebaseMobile Development

Three years ago, I was trying to send a push notification to a test device for Greeny Corner (my plant care app) and spent 14 hours straight chasing dead ends. Android Studio kept spitting out cryptic errors, iOS asked for certificates I didn’t know how to generate, and the Expo documentation felt like solving a riddle written in disappearing ink. Today, with 40+ projects under my belt (including a limo booking platform that fires 15k notifications daily during Ramadan), I want to save you that pain.

Setting Up the Right Environment

If you’re starting fresh, stop what you’re doing and use Expo Application Services (EAS) Build. The classic exp build is like trying to drive a Ferrari with square wheels – it works, but why suffer? EAS handles all the provisioning profiles and Android keystores without making you Google how to decode .pem files into something human.

First, make sure your app.json specifies a runtime version that plays nice with push APIs. For Greeny Corner, we locked in SDK 54 before launch – turned out to be a good move since Apple started rejecting apps using older notification protocols six months later. Speaking of Apple, their App Store review bot once rejected a build for using the Development APNs environment by mistake. Fun times.

Android Needs Firebase (and Patience)

When setting up Firebase Cloud Messaging for Android, you’ll download a google-services.json file. Keep this in your project root without committing it to version control. I learned this the hard way when a client’s production app leaked an API key after I forgot to .gitignore it – 2AM debugging session ensued.

Wait, did you generate a SHA-256 certificate fingerprint for Firebase? I didn’t, because why would the FCM setup guide not mention this in bold?! A client in Dubai called me at 9 PM because their notifications only worked on emulators, not physical devices. Turns out the SHA hash was missing – use this Terminal command:

bash
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

If the words "keytool" and "keystore" make you want to cry, join the club.

iOS Certificates: Fun For the Whole Family

For Apple, create an Apple Push Notification service (APNs) key in your developer account. This .p8 file is like your project’s soul – guard it with your life. When localizing notifications for an Abu Dhabi client who needed Arabic translations in the notification body, suddenly every edge case became a linguistic puzzle.

Quick note: Expo’s managed workflow auto-handles the aps-environment entitlement, but if you detatch to bare workflow (don’t unless you have to), you’ll fight Xcode to recognize your .p8 file. Been there, drank that coffee.

Coding the Notification Logic

Install the expo-notifications package – seriously, don’t try using expo push send without it. Here’s how we handle permissions in Greeny Corner:

javascript
import * as Notifications from 'expo-notifications';

Notifications.requestPermissionsAsync().then((status) => {
  if (status.granted) {
    // Save their token somewhere secure!
  }
});

Wait, you remembered to add the NOTIFICATIONS usage description in Info.plist? No? That’s why your iOS dev build asks permission after launch instead of when you click "Enable Reminders" like your UAE client specifically requested.

Sending Your First Notification

Expo’s built-in Push Notification Tool is okay™. For production, though, start with a simple curl command to avoid relying on the Expo dashboard like it’s 2020:

bash
curl -H "Content-Type: application/json" -X POST "https://exp.host/--/api/v2/push/send" -d '{
  "to": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]",
  "title": "This better work",
  "body": "Hello UAE developer friend 🇦🇪",
  "data": {"deepLink": "greenycorner://reminder/123"}
}'

When testing push delivery times for Tawasul Limo’s rush hour bookings, this method uncovered delays caused by using the wrong push channel in Saudi Arabia’s local telco networks. Turns out regional network quirks matter more than anyone wants to admit.

Real-World Gotchas

  • Notifications work differently in dev mode – Android shows them while in foreground, iOS doesn’t unless you explicitly code the handler.
  • Always check time zones when scheduling. A Kuwait client once called in panic because a notification was sent at 3 AM local time instead of 3 PM. Turns out we were using UTC without conversion.
  • Firebase and Expo’s token refresh system sometimes gives expired credentials. We now refresh tokens every 24 hours in the background. Don’t trust the initial token forever.

Production Pitfalls

When a UAE e-commerce company launched last year, they hardcoded the Expo push token API URL. Predictably, it broke after 10k users. Their DevOps guy called me sobbing into WhatsApp – turns out you need to rate-limit your push queue. Who knew?

Final Thoughts (That Aren’t Generic)

When it all works, push notifications feel like magic. When they don’t? You’re staring at cryptic Firebase logs while the Qatar World Cup app crashes because of a misconfigured API key.

If you’re building for the GCC market, remember: Arabic text direction matters in notification rendering, and local telcos sometimes cache push traffic (which explains 10% of the "notifications not delivered" tickets we see in Ramadan).

You don’t need a "holistic push notification strategy" – you need it to work at 2 AM when the client in Jeddah needs an urgent fix. This guide would’ve saved me days back when I ruined that perfectly good cup of Turkish coffee fighting an expired .pem file.

S

Sarah

Senior Full-Stack Developer & PMP-Certified Project Lead — Abu Dhabi, UAE

7+ years building web applications for UAE & GCC businesses. Specialising in Laravel, Next.js, and Arabic RTL development.

Work with Sarah