Sending push notifications

This post is part of a series, where I document my experiences learning React Native while building my first-ever mobile app. This is also my way of giving back to the React Native community, with tips and insights I note along the journey.

Push notifications are a great tool to engage users with your app. For instance, in our app, we’d like to send them a push notification every time we add a new list in the system. In this post we’ll see how to do this. Spoiler: it’s pretty easy!

There are a few things to get out of the way before we start implementing push notifications in our app. First, push notification permissions don’t work on emulators, so we’ll need to use a real device to test this. We already covered in our assumptions that we’ll be using a real device with the Expo app running on it, so this should simple.

Second, we’ll also need a running server with a public IP address. In this post, I’ll be using AWS Lambda for this. More specifically, I’ll be using the Node.js starter template from our trusted serverless guide. You can feel free to use whatever alternative you feel more comfortable in. In fact, one great option to use during development is setting up ngrok to expose a tunnel from localhost. Either way, I don’t plan to cover the backend implementation here, as it’s quite a bit out of scope.

Finally, we assume that the user has given our app permissions to receive push notifications. We covered asking for permissions earlier when we discussed getting geolocation information, so code below should seem quite familiar:


OK, now that we have these out of the way, let’s get to the juicy details!

Before we’re able to send any push notifications to a device, we need to know a unique identifier for this device. This is called the Expo Push Token and it’s unique for each device your app is installed on. The diagram below from the Expo docs illustrates this handshake:

Image for post
Image for post

We’ll actually do this is in the registerForPushNotificationsAsync method:


We’re now ready to send notifications from our backend server!

Whenever we want to send a push notification to a specific device, we just use the Expo Push Token with the Expo API call in a simple HTTP POST request.

The following diagram shows the lifecycle of a push notification using Expo:

Image for post
Image for post

There are libraries for most popular backend languages that take care of this (e.g. Node.js, Ruby, python, Rust, Go, Elixir) — the official docs have links to all of these. However, in our example, we’ll use plain-old, handcrafted HTTP requests directly to the Expo backend. To do this we need to send a POST request to with these headers:

Content-Type: application/json
Accept: application/json
Accept-Encoding: gzip, deflate

The body of your request needs to include the Expo Push Token, a title, and a body, as shown in the example below:

During development, you can even use the Expo push notification tool, which allows you to send push notifications from a web page. Super useful!

The API call actually returns an array of push tickets. Each push ticket indicates whether Expo successfully received the notification and, when successful, a receipt ID. As the documentation points out, the fact that Expo successfully received the notification doesn’t mean that the notification will be delivered nor that the device has received the message. It just means that “I received your notification and I’ve enqueued for delivery”.

Finally, we can then use those receipt IDs to query the iOS or the Android push notification service about the status of those requests. The Expo docs have more details about this, which are worth checking out.

OK, great — we’re able to send push notifications to devices that we have in our system. Now, let’s ensure that our app is listening for those notifications.

In order for our app to listen for notifications, we’ll need to set up a handler that gets called each time our app receives a notification. For simplicity, let’s assume that we’ll simply store the notification in the state. Registering the handler is commonly done in the componentDidMount lifecycle method:

If we want to be good citizens, it’s best if we remove the listener in the componentWillUnmount lifecycle method by calling the remove() function on the notificationSubscription object.

In this post, we covered the basics of how to send push notifications to devices using the Expo API and backend service. Although there are a few nuances (mostly platform specific) that we didn’t cover, this should get you quite far. This can be quite a tricky thing to tackle, but thankfully, Expo makes it really easy. I would urge you to read up more on the API details, especially around notification timing, on the official docs. Enjoy & happy hacking!

If you enjoyed this article, feel free to hit that clap button 👏 to help others find it.

I’m Alex, a software engineer and startup geek. Currently, I’m a co-founder at a startup called AgentRisk, where I’m the VP of Product & Engineering. In a past life, I was part of the founding engineering team at an enterprise cloud storage startup in Silicon Valley, did a bunch of cool cutting-edge projects at Cisco Research, and worked on some really innovative data center network architectures while doing my Master’s at UCSD. I always enjoy learning new technologies and b̵u̵i̵l̵d̵i̵n̵g̵ shipping side-projects all the time.

Co-founder & CTO @ AgentRisk. Former infra-tech guy (storage, networks). Startup nerd. Always building cool side-projects. #LongLA

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store