Getting started: Push Notifications in React Native
This guide will get you started with Ably Push Notifications in a new React Native application, using the ably/react-native-push plugin.
You'll learn how to set up your application with Firebase Cloud Messaging (FCM), activate push notifications on the device, publish push notifications, subscribe to channel-based push, and handle incoming notifications on both iOS and Android.
Prerequisites
- Sign up for an Ably account.
- Create a new app, and create your first API key in the API Keys tab of the dashboard.
- Your API key needs the
publish,subscribe, andpush-subscribecapabilities. - Also add the
push-admincapability if you're using the same API key to publish a push notification. In production this would more likely be a server using a different API key.
- Add a rule to a channel so you can test sending push notification via a channel. Select Rules in the Ably dashboard, add a new rule and enable the Push notifications option.
- Install Node.js 18 or higher.
- Set up your React Native development environment following the React Native CLI Quickstart.
- For iOS: Install Xcode. Push notifications require a physical iOS device (simulators do not support push).
- For Android: Install Android Studio. Use a physical device or an emulator with Google Play Services installed.
(Optional) Install Ably CLI
Use the Ably CLI as an additional client to quickly test Pub/Sub features and push notifications.
ably init installs the Ably CLI, authenticates, and sets the default app and API key in a single command:
npx @ably/cli initSet up Firebase Cloud Messaging
Firebase Cloud Messaging delivers push notifications for both Android and iOS. To enable FCM:
- Go to the Firebase Console and create a new project (or use an existing one).
- Register your Android app using your package name. Download
google-services.jsonand place it inandroid/app/. - Download your Firebase service account JSON file from your Firebase console: Project configuration → Service Accounts → Generate new private key.
- In the Ably dashboard left sidebar, navigate to your app's Push Notifications.
- Scroll to the Configure push service for devices section and press Configure Push.
- Upload your Firebase service account JSON file in Setting up Firebase Cloud Messaging section.
- In the Apple Developer portal, go to Certificates, Identifiers & Profiles → Keys.
- Add a new key and check Apple Push Notifications service (APNs), click Register.
- Download the
.p8file — you can only download it once. Note your Key ID and Team ID. - In the Firebase Console, go to Project configuration → Cloud Messaging → Apple App Setup → APNS authentication key to upload your
.p8file. - Register an iOS app in your Firebase project using your bundle identifier. Download
GoogleService-Info.plistand add it to your Xcode project's root target.
Create a React Native project
Create a new React Native project and install the required dependencies:
npx react-native@latest init PushTutorial
cd PushTutorial
npm install ably @react-native-firebase/app @react-native-firebase/messaging @react-native-async-storage/async-storage @notifee/react-nativeConfigure Android project
Apply the Google Services plugin in android/build.gradle:
// android/build.gradle
buildscript {
dependencies {
classpath('com.google.gms:google-services:4.4.2')
}
}Then apply it in android/app/build.gradle:
// android/app/build.gradle
apply plugin: 'com.google.gms.google-services'Also declare the POST_NOTIFICATIONS permission in android/app/src/main/AndroidManifest.xml:
1
2
3
4
5
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
...
</manifest>Configure iOS project
Open ios/PushTutorial.xcworkspace in Xcode and add the Push Notifications capability: select your target, go to Signing & Capabilities, and click + Capability.
Add use_modular_headers! to ios/Podfile after prepare_react_native_project!:
prepare_react_native_project!
use_modular_headers!This is required for Firebase Swift pods (FirebaseCoreInternal, GoogleUtilities) to be integrated as static libraries. Then install the native pods:
cd ios && pod install && cd ..Then add FirebaseApp.configure() to AppDelegate.swift before React Native starts:
import Firebase
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
FirebaseApp.configure()
// ... rest of existing setup
}Add all further code to App.tsx.
Step 1: Set up Ably
Replace the contents of App.tsx with the following to create the push plugin with ReactNativePush.create(), initialize the Ably Pub/Sub client with it, wrap the app in AblyProvider and ChannelProvider, and subscribe to realtime messages on the channel with useChannel:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import React, {useState} from 'react';
import {
Platform,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import {SafeAreaView} from 'react-native-safe-area-context';
import * as Ably from 'ably';
import ReactNativePush from 'ably/react-native-push';
import {AblyProvider, ChannelProvider, useAbly, useChannel} from 'ably/react';
import AsyncStorage from '@react-native-async-storage/async-storage';
import messaging from '@react-native-firebase/messaging';
import notifee, {AuthorizationStatus} from '@notifee/react-native';
const CHANNEL_NAME = 'my-first-push-channel';
// The push plugin persists device state in the storage you supply and calls
// requestToken whenever it needs a push token during activation
const Push = ReactNativePush.create({
storage: AsyncStorage,
requestToken: async () => ({
transportType: 'fcm', // messaging().getToken() returns an FCM token on both Android and iOS
token: await messaging().getToken(),
}),
});
// Use token authentication in production
const client = new Ably.Realtime({
key: 'demokey:*****',
clientId: 'push-tutorial-client',
plugins: {Push},
});
function PushScreen() {
const [status, setStatus] = useState('Ready to start');
const [log, setLog] = useState<string[]>([]);
function addLog(message: string) {
setLog(prev => [...prev, message]);
}
function showStatus(message: string) {
setStatus(message);
console.log(message);
}
useChannel(CHANNEL_NAME, message => {
addLog(`Received: ${message.name} - ${JSON.stringify(message.data)}`);
});
return (
<SafeAreaView style={styles.safeArea}>
<View style={styles.container}>
<Text style={styles.title}>Ably Push Tutorial</Text>
<View style={styles.statusBox}>
<Text style={styles.statusText}>{status}</Text>
</View>
<ScrollView style={styles.logBox}>
{log.map((entry, i) => (
<Text key={i} style={styles.logEntry}>{entry}</Text>
))}
</ScrollView>
</View>
</SafeAreaView>
);
}
export default function App() {
return (
<AblyProvider client={client}>
<ChannelProvider channelName={CHANNEL_NAME}>
<PushScreen />
</ChannelProvider>
</AblyProvider>
);
}
const styles = StyleSheet.create({
safeArea: {flex: 1, backgroundColor: '#fff'},
container: {flex: 1, padding: 16},
title: {fontSize: 22, fontWeight: 'bold', textAlign: 'center', marginBottom: 12},
statusBox: {backgroundColor: '#f0f0f0', padding: 12, borderRadius: 6, marginBottom: 12},
statusText: {fontSize: 14},
logBox: {flex: 1, backgroundColor: '#fff', borderWidth: 1, borderColor: '#ddd', borderRadius: 6, padding: 8},
logEntry: {fontFamily: Platform.OS === 'ios' ? 'Courier' : 'monospace', fontSize: 12, marginBottom: 4},
});Key configuration options:
key: Your Ably API key.clientId: A unique identifier for this client.plugins: The push plugin created withReactNativePush.create(). Your app supplies the two things the SDK cannot provide itself on React Native: persistent storage (any implementation of the@react-native-async-storage/async-storageinterface) and arequestTokencallback that returns a push token. ReturntransportType: 'fcm'for a Firebase Cloud Messaging registration token, ortransportType: 'apns'for a raw APNs device token obtained frommessaging().getAPNSToken().
AblyProvider makes the Ably client available to all child components via React context. ChannelProvider scopes child components to my-first-push-channel. useChannel subscribes to realtime messages published on the channel.
Step 2: Set up push notifications
Activate push notifications with push.activate(). Your app must request notification permission from the user before activation. Add the following inside PushScreen:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// Inside PushScreen:
const client = useAbly();
async function requestPermission(): Promise<boolean> {
if (Platform.OS === 'android') {
// Use notifee for consistent POST_NOTIFICATIONS permission behavior across Android API levels
const settings = await notifee.requestPermission();
return settings.authorizationStatus >= AuthorizationStatus.AUTHORIZED;
}
// On iOS, request permission using Firebase Messaging which will trigger the native iOS permission dialog
const authStatus = await messaging().requestPermission();
return (
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL
);
}
async function activatePush() {
try {
showStatus('Activating push notifications...');
const granted = await requestPermission();
if (!granted) {
showStatus('Notification permission denied.');
return;
}
await messaging().registerDeviceForRemoteMessages(); // Required to receive push notifications on iOS, no-op on Android
await client.push.activate();
const device = await client.getDevice();
showStatus(`Push activated. Device ID: ${device.id}`);
addLog(`Push activated. Device ID: ${device.id}`);
} catch (error) {
showStatus(`Failed to activate push: ${(error as Ably.ErrorInfo).message}`);
}
}
async function deactivatePush() {
try {
showStatus('Deactivating push notifications...');
await client.push.deactivate();
showStatus('Push notifications deactivated.');
} catch (error) {
showStatus(`Failed to deactivate push: ${(error as Ably.ErrorInfo).message}`);
}
}client.push.activate() does the following:
- Generates a unique device identifier and secret, and persists them in the storage you supplied to
ReactNativePush.create(). - Calls your
requestTokencallback to obtain the FCM registration token. - Registers the device with Ably's push notification service using the token.
- Persists the resulting device identity, so the registration survives app restarts. Calling
activate()again on a registered device resolves immediately.
After successful activation, use client.getDevice() to read the device's state, including the id that push notifications can be published to.
Keep the push token up to date
FCM can rotate the device's push token at any time. When that happens, the token registered with Ably no longer matches the device and push notifications silently stop arriving. Forward rotated tokens to Ably with push.updateToken(), which updates the device registration in place.
1
2
3
4
5
6
useEffect(() => {
const unsubscribe = messaging().onTokenRefresh(async token => {
await client.push.updateToken({transportType: 'fcm', token});
});
return unsubscribe;
}, []);Handle push notifications
The FCM SDK handles background push notifications automatically and displays them as system notifications. For foreground handling, use @notifee/react-native to display notifications while the app is open.
Add the following inside PushScreen:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
useEffect(() => {
// Create a default Android notification channel
if (Platform.OS === 'android') {
notifee.createChannel({id: 'default', name: 'Default Channel'});
}
// Handle foreground push messages
const unsubscribe = messaging().onMessage(async remoteMessage => {
const title = remoteMessage.notification?.title ?? 'Push Notification';
const body = remoteMessage.notification?.body ?? '';
addLog(`Push received: ${title} — ${body}`);
await notifee.displayNotification({
title,
body,
android: {channelId: 'default'},
});
});
return () => {
unsubscribe();
};
}, []);Step 3: Subscribe to channel push notifications
To subscribe your device to a channel so it can receive channel-based push notifications, use channel.push.subscribeDevice(). The device must be activated first. Add the following functions inside PushScreen, after the functions from Step 2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async function subscribeToChannel() {
try {
await client.channels.get(CHANNEL_NAME).push.subscribeDevice();
showStatus(`Subscribed to push on channel: ${CHANNEL_NAME}`);
} catch (error) {
showStatus(`Failed to subscribe: ${(error as Ably.ErrorInfo).message}`);
}
}
async function unsubscribeFromChannel() {
try {
await client.channels.get(CHANNEL_NAME).push.unsubscribeDevice();
showStatus(`Unsubscribed from push on channel: ${CHANNEL_NAME}`);
} catch (error) {
showStatus(`Failed to unsubscribe: ${(error as Ably.ErrorInfo).message}`);
}
}Step 4: Build the UI
Build a UI in your app to add buttons that call all push functions. Update the return statement in PushScreen:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
return (
<SafeAreaView style={styles.safeArea}>
<View style={styles.container}>
<Text style={styles.title}>Ably Push Tutorial</Text>
<View style={styles.statusBox}>
<Text style={styles.statusText}>{status}</Text>
</View>
<View style={styles.buttons}>
<TouchableOpacity style={[styles.btn, styles.btnGreen]} onPress={activatePush}>
<Text style={styles.btnText}>Activate Push</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.btn, styles.btnRed]} onPress={deactivatePush}>
<Text style={styles.btnText}>Deactivate Push</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.btn, styles.btnPurple]} onPress={subscribeToChannel}>
<Text style={styles.btnText}>Subscribe to Channel</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.btn, styles.btnOrange]} onPress={unsubscribeFromChannel}>
<Text style={styles.btnText}>Unsubscribe from Channel</Text>
</TouchableOpacity>
</View>
<ScrollView style={styles.logBox}>
{log.map((entry, i) => (
<Text key={i} style={styles.logEntry}>{entry}</Text>
))}
</ScrollView>
<TouchableOpacity style={[styles.btn, styles.btnGray]} onPress={() => setLog([])}>
<Text style={styles.btnText}>Clear Log</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);Add the button styles to the StyleSheet.create call:
1
2
3
4
5
6
7
8
buttons: {gap: 8, marginBottom: 12},
btn: {padding: 14, borderRadius: 6, alignItems: 'center'},
btnText: {color: '#fff', fontWeight: '600'},
btnGreen: {backgroundColor: '#28a745'},
btnRed: {backgroundColor: '#dc3545'},
btnPurple: {backgroundColor: '#6f42c1'},
btnOrange: {backgroundColor: '#fd7e14'},
btnGray: {backgroundColor: '#6c757d', marginTop: 8},Build and run your app on a physical device:
# Android
npx react-native run-android
# iOS
npx react-native run-ios --deviceYou can also open each platform project in their respective IDEs and run from there.
Step 5: Publish a push notification
In the app tap Activate Push and wait until the status message displays your device ID.
Publish directly to your device
Publish a push notification directly to your client ID (or device ID using --device-id instead of --client-id) via the Ably CLI:
ably push publish --client-id push-tutorial-client \
--title "Hello" \
--body "World!" \
--data '{"foo":"bar"}'Publish via a channel
Tap Subscribe to Channel in the app, then publish a push notification to the channel using the Ably CLI:
ably push publish --channel my-first-push-channel \
--title "Hello" \
--body "World!" \
--message '{"name":"greeting","data":"Hello World!"}'If you tap Unsubscribe from Channel, the device no longer receives push notifications for that channel. Run the same command again and verify that no notification is received.
To see the full list of options for publishing push notifications with the Ably CLI, run ably push publish --help or see the Ably CLI push documentation. To publish push notifications from your own server code instead of the CLI, see Push notification publishing.
Next steps
- Understand token authentication before going to production.
- Explore push notification administration for managing devices and subscriptions.
- Learn about channel rules for channel-based push notifications.
- Read more about the Push Admin API.
You can also explore the Ably JavaScript SDK on GitHub, or visit the API references for additional functionality.