I spent 8 straight hours trying to make push notifications work in a React Native app for a client in Sharjah last year. The app was for a plant care service — they wanted reminders to hit users at the exact time their aloe vera needed watering. Simple, right? No. Expo’s SDK was throwing errors I’d never seen, iOS permissions were a lottery, and the client kept asking (in Arabic, which I’m fluent in) why the flowers kept dying even though the app was live.
It’s 2026 now, and I’ve finally figured out a workflow that works. Let’s fix what the Expo docs don’t.
Getting Started: What You Need Before Writing One Line
The good news? Setting up push notifications in Expo isn’t magic. The bad news? It’s not a checkbox item either. You’ll need:
- A managed Expo project running SDK 49 or higher (I use 54 for Greeny Corner)
- Android/iOS credentials (we’ll dive into this later)
- Patience. Especially for iOS — Apple’s notification system is built to test your soul.
Install expo-notifications first. This isn’t optional, and yes — you’ll import it even if you’re using Firebase downstream. They’re not the same thing.
expo install expo-notificationsStep 1: Handle Permissions — The Confusing Part
Start by requesting permissions right after app launch. Most tutorials skip this, then wonder why users get a prompt 30 seconds later (spoiler: they don’t).
I put this in my root layout provider:
useEffect(() => {
registerForPushNotificationsAsync();
}, []);
async function registerForPushNotificationsAsync() {
const { status } = await Notifications.requestPermissionsAsync();
if (status !== 'granted') {
alert('Enable notifications to keep your plants alive!');
return;
}
}This isn’t a guarantee they’ll stay granted. I had a Qatari client’s CEO uninstall the app after notifications stopped working — turned out he’d hit “don’t allow” during a demo. You’ll need to check permission status regularly.
Step 2: Generate the Push Token — Not as Easy as It Sounds
Call getDevicePushTokenAsync() from expo-notifications:
import * as Notifications from 'expo-notifications';
export async function getExpoPushToken() {
const pushToken = await Notifications.getDevicePushTokenAsync();
return pushToken.data;
}Hold up — this will return garbage if you’re not using a real device. Simulator? Fakes tokens. Web testing? Don’t bother.
Here’s what happened to me: built a proof of concept for a UAE real estate app (Reach Home Properties) on Expo web. Everything looked good. Deployed to clients. iOS users got nothing. The token validation was client-side. Don’t do that.
Dev vs Production: Why Does It Work in Metro But Not Standalone?
Ah yes, the classic “works in dev, fails in prod” issue. Your app.json needs extra sprinkles:
{
"expo": {
"name": "Greeny Corner",
"plugins": [
[
"expo-build-properties",
{
"ios": {
"supports32Bit": false,
"requireFullScreen": true
}
}
]
]
}
}iOS requires supports32Bit set to false for push tokens to work. I spent two days on a limo booking app (Tawasul Limo) before finding this buried in an Apple dev forum. Don’t let that be you.
Sending Notifications: Testing Without Losing Your Mind
Don’t use expo-cli commands. They’re deprecated. Use Expo’s API or a service like Courier.
curl -H "Content-Type: application/json" -X POST "https://exp.host/--/api/v2/push/send" -d '{
"to": "ExponentPushToken[XXXXXXXXXXXXXXXXXXXXXX]",
"title": "Hey there",
"body": "Water your aloe vera",
"data": {"plantId": "123"}
}'Wait — your token starts with ExponentPushToken? Good. If it starts with ExpoPushToken, you’re in Expo Go and it won’t work.
Test with Expo Notifier. It’s in Apple’s App Store because Expo made this tool. Use it. Save your sanity.
Handling Time Zones in the UAE
A client in Dubai once asked why their users got push reminders at 3 AM. Turns out, the server was sending timestamps in UTC.
Here’s how to respect local time zones in notifications:
const trigger = new Date();
trigger.setHours(9); // 9 AM local time
Notifications.scheduleNotificationAsync({
content: { title: 'Water your plant' },
trigger
});If your users span the GCC, always store their current time zone and adjust triggers accordingly. This isn’t built-in — you’ll have to calculate offsets manually. Fun.
One Last Gotcha: Notifications Vanish on iOS When the App is Fully Closed
Yep, Apple kills background processes aggressively. The fix? You’ll need to set up a VoIP push. It’s involved and out of scope here, but I wrote a thread on Twitter about it if you’re pulling out your hair on a Wednesday (which you will be).
If you’re building something in Expo now, don’t expect perfection. When Greeny Corner launched, notifications failed for 15% of users after we changed the app icon. We had to manually resubmit the new asset to Expo’s servers. No error showed up — we found it by comparing MD5 hashes of config files.
This isn’t a perfect system. It’s a system that works if you remember to care about the edge cases.
Need help shipping this? Hit me up at sarahprofile.com/contact. I’m probably wrestling with Firebase Cloud Messaging settings for another limo booking app — but I’ll answer. 😊