Vous êtes probablement à un point où l'application fonctionne, les utilisateurs sont connectés et le produit souhaite des flux de re-engagement qui ressemblent à des applications natives. Les rappels de panier. Les invitations de révision. Les alertes de nouveaux messages. Les annonces de mise à jour. La première impulsion est souvent de « simplement brancher les notifications push », puis une semaine plus tard, vous êtes en train de déboguer pourquoi un appareil reçoit des alertes, le simulateur semble s'enregistrer correctement, et personne ne peut expliquer pourquoi les appuis-touche ne lancent pas la bonne écran.
C'est là que les notifications push d'Expo sont soit agréablement simples, soit surprenamment fragiles.
Expo donne aux équipes React Native une couche pratique sur APNs et FCM, ce qui est exactement pourquoi tant d'équipes l'utilisent. Mais la différence entre une démo et une mise en production prête à l'emploi est réelle. Le cycle de vie des jetons, la synchronisation des permissions, la configuration des écouteurs, la conception des payloads et la suppression des données du serveur comptent tous. Si vous faites également des changements fréquents de logique d'application, le besoin de discipline opérationnelle devient encore plus aigu, surtout si votre travail de retenue des utilisateurs dépend de la messagerie fiable et de la vitesse de mise à jour. C'est la même préoccupation plus large derrière le travail de retenue des utilisateurs de l'application mobile: la livraison n'est utile que si l'expérience utilisateur autour d'elle est prévisible.
Table des matières
- La base pour engager les utilisateurs avec les notifications push d'Expo
- Configuration initiale du projet et de configuration
- Demander la permission et capturer les jetons de push
- Envoyer des notifications à partir de votre serveur
- Gestion des notifications entrantes dans votre application
- Meilleures pratiques de production et pièges courants
The Fondation pour engager les utilisateurs avec les notifications Push d'Expo
Une notification push d'Expo La mise en place est attrayante pour une raison principale. Elle élimine une grande partie de la complexité de la messagerie native que les équipes ne veulent pas gérer dès le premier jour. Au lieu de construire directement les canaux APNs et FCM, vous pouvez travailler avec la passerelle d'Expo et vous concentrer sur le comportement du produit, la routage, l'UX des permissions et la logique des messages backend.
Cette abstraction ne rend pas les notifications push « moins réelles ». Elle change simplement où va l'effort d'ingénierie.
Le service est également suffisamment rapide pour que les performances ne soient généralement pas la première chose à craindre. De mars 14, 2023 à juin 12, 2023, les notifications push d'Expo API ont montré un 42 milliseconde temps de réponse médian, 273 milliseconde p99 de latenceet une taux d'erreur quotidien moyen de 0,17% sur des dizaines de millions de messages quotidiensSelon Knock’s analyse de benchmark de __CAPGO_KEEP_0__ de l’envoi de notifications Expo. Knock’s Expo push API benchmark analysisCe que Expo abstrait réellement
Lorsque les équipes disent « Expo push », elles ont souvent en tête plusieurs préoccupations séparées regroupées ensemble :
La mise en œuvre de la navigation :
- Expo transmet des messages à APNs pour iOS et FCM pour Android. La forme du jeton :
- La mise en œuvre de la navigation : Votre serveur stocke et envoie un jeton de poussée Expo au lieu de gérer d'abord la gestion de jetons spécifiques au plateau.
- Contrat de demande : Vous envoyez un payload à Expo’s push API au lieu d'intégrer directement les API de fournisseur native.
C'est utile, mais cela crée également une confusion courante. Les équipes supposent parfois que Expo est responsable de tous les problèmes de livraison. En pratique, de nombreux échecs proviennent de l'application code, de jetons périmés, de payloads malformés ou d'une mauvaise gestion des autorisations.
Règle pratique : Traitez Expo comme un couche de transport fiable, et non comme un substitut d'une conception client et serveur solide.
Ce que signifie vraiment la maturité de production
Un démo fonctionnel prouve seulement que l'un des appareils a accepté un payload une fois. La maturité de production signifie autre chose :
| Préoccupation | Esprit de démo | Esprit de production |
|---|---|---|
| Autorisations | Demander immédiatement | Demander dans le contexte, après que la valeur de l'utilisateur soit claire |
| Jetons | Enregistrer une fois | Rafraîchir, dédoubler, expirer et réconcilier |
| Payloads | Placer tout dans data |
Gardez les payloads petits et axés sur l'action |
| Comportement de l'application | Afficher un avertissement | Se déplacer correctement et gérer l'état en avant-plan |
| Opérations | Tests de manuel | Reçus, nettoyage, journaux, et gestion d'incident |
C'est la différence entre « notifications envoyées » et « notifications supportent un flux de workflow de produit réel. »
Configuration et Initialisation du Projet
Un grand nombre de douleurs d'envoi de push Expo commencent avant la première invitation de permission. Si votre configuration de projet est négligée, le client code peut sembler correct tandis que l'application se comporte de manière incohérente entre les builds.

Commencez par les bibliothèques correctes installées et un environnement de développement qui correspond à votre chemin de build. Si vous travaillez au-delà d'Expo Go, il est utile de synchroniser votre flux de travail local avec une configuration de client de développement Expo personnalisée, car le comportement des notifications doit souvent être validé dans une build qui reflète plus étroitement la production qu'une rapide exécution de sandbox.
Installez les packages de notification
Au minimum, vous aurez généralement besoin de :
expo-notificationspour les demandes de permission, la récupération de jeton, les écouteurs et la présentation de notification.expo-deviceparce que vous devriez protéger la récupération de jetonsDevice.isDevice.
Les commandes d'installation typiques dépendent de votre gestionnaire de packages, mais la clé est l'alignement de version avec votre Expo SDK. N'allez pas mixez des versions de packages arbitraires. Laissez Expo résoudre les versions compatibles.
Ajoutez une configuration au niveau du projet
Votre config doit être explicite. Un minimum app.json ou app.config.js devrait refléter le fait que les notifications font partie de votre contrat d'application, et non une pensée après-coup.
{
"expo": {
"name": "MyApp",
"slug": "my-app",
"plugins": ["expo-notifications"],
"ios": {
"bundleIdentifier": "com.example.myapp"
},
"android": {
"package": "com.example.myapp"
},
"extra": {
"eas": {
"projectId": "your-project-id"
}
}
}
}
Un ou deux détails importent ici :
- Les identifiants de bundle et les noms de packages doivent correspondre à l'application que vous envoyez réellement.
- Le plugin de notifications s'assure que le projet natif obtient la mise en place requise lors de la construction.
- L'ID du projet EAS Les choses comptent lorsque la récupération du jeton s'attend à ce que l'application soit associée au projet Expo correct.
Configurer un gestionnaire de notification dès le début
Beaucoup de tutoriels de base attendent trop longtemps pour définir le comportement de notification. N'y allez pas. Placez-le près du démarrage de l'application afin que le comportement en avant-plan soit prévisible.
import * as Notifications from 'expo-notifications';
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: true,
}),
});
Les équipes décident si les notifications en avant-plan doivent afficher un avertissement, jouer un son ou affecter les badges. Le comportement exact dépend de votre produit. Une application de messagerie et une application de paiement ne feront pas les mêmes choix.
Si vous ne définissez pas intentionnellement le comportement en avant-plan, votre équipe finira par déboguer les « notifications manquantes » qui ont été effectivement reçues mais n'ont jamais été présentées comme attendu par le produit.
Android nécessite une configuration de canal
Les canaux de notification Android ne sont pas optionnels en pratique. Si vous les ignorez, vos avertissements peuvent ressembler à des incohérences ou ne pas correspondre aux attentes des utilisateurs.
import { Platform } from 'react-native';
import * as Notifications from 'expo-notifications';
export async function configureAndroidNotifications() {
if (Platform.OS !== 'android') return;
await Notifications.setNotificationChannelAsync('default', {
name: 'Default',
importance: Notifications.AndroidImportance.MAX,
});
}
Configurez cela pendant l'initialisation de l'application. Ensuite, maintenez les identifiants de canal stables. Les modifier sans réfléchir rend le comportement de notification plus difficile à raisonner plus tard.
Demander la permission et capturer les jetons de notification
C'est là que beaucoup d'équipes copient un snippet, puis finissent par regretter.
Les demandes de permission nécessitent un timing, une prise en compte de la plateforme et un traitement asynchrone discipliné. La capture de jetons doit se produire uniquement sur un appareil physique, uniquement après la résolution des permissions, et uniquement si vous êtes prêt à stocker le résultat sur votre serveur backend immédiatement.

The fonction client qui devrait être votre point de départ
Utilisez une fonction comme celle-ci comme point de départ :
import * as Device from 'expo-device';
import * as Notifications from 'expo-notifications';
import Constants from 'expo-constants';
import { Platform } from 'react-native';
type RegisterResult =
| { ok: true; token: string }
| { ok: false; reason: string };
export async function registerForExpoPushNotificationsAsync(): Promise<RegisterResult> {
if (!Device.isDevice) {
return { ok: false, reason: 'Push notifications require a physical device.' };
}
if (Platform.OS === 'android') {
await Notifications.setNotificationChannelAsync('default', {
name: 'Default',
importance: Notifications.AndroidImportance.MAX,
});
}
const permissions = await Notifications.getPermissionsAsync();
let finalStatus = permissions.status;
if (finalStatus !== 'granted') {
const request = await Notifications.requestPermissionsAsync();
finalStatus = request.status;
}
if (finalStatus !== 'granted') {
return { ok: false, reason: 'Notification permission was not granted.' };
}
const projectId =
Constants.expoConfig?.extra?.eas?.projectId ??
Constants.easConfig?.projectId;
if (!projectId) {
return { ok: false, reason: 'Missing EAS project ID configuration.' };
}
const tokenResponse = await Notifications.getExpoPushTokenAsync({ projectId });
return { ok: true, token: tokenResponse.data };
}
L'ordre compte. Vous vérifiez le type de périphérique en premier, configurez le comportement du canal Android, résolvez les permissions, validez la configuration du projet, puis demandez le jeton Expo.
Pourquoi Device.isDevice n'est pas facultatif
C'est l'une des rares erreurs qui créent beaucoup de bruit tout en paraissant innocente. Les équipes expertes ne demandent la permission que conditionnellement lorsque Device.isDevice est vrai, et un piège commun est de passer outre ce garde, ce qui pousse les développeurs à envoyer des notifications à des jetons de simulateur invalides et à blâmer Expo lorsque le problème est vraiment la configuration de l'application, comme décrit dans les notes d'implémentation de notifications Expo d'Eagerworks.
C'est pourquoi le contrôle se trouve en haut de la fonction. Ne le cachez pas derrière un helper. Faites-le évident.
Les résultats de la simulation sont utiles pour les tests de l'interface utilisateur. Ils ne sont pas fiables pour valider l'enregistrement du jeton de notification.
Demandez la permission au bon moment
Don’t ask sur l’écran d’accueil. Don’t demander avant que l’utilisateur comprenne la valeur. Le meilleur moment est généralement après une action de l’utilisateur qui rend les avantages des notifications concrètes, comme activer les mises à jour de livraison, rejoindre une conversation ou sauvegarder un élément surveillé.
Une bonne mise en œuvre suit généralement ce flux :
- L'utilisateur atteint une limite de fonctionnalité significative.
- L'application explique la valeur des notifications dans son propre UI.
- L'application demande la permission système.
- L'application stocke le jeton sur le serveur immédiatement si la permission est accordée.
Ce dernier pas est là où beaucoup d'applications échouent. Elles récupèrent le jeton, le logent localement et reportent l'enregistrement sur le serveur. Plus tard, le support ne peut pas déterminer quel appareil avait quel jeton à quel moment.
Ici est un exemple simple de stockage du jeton après l'enregistrement :
export async function enablePushForCurrentUser(userId: string) {
const result = await registerForExpoPushNotificationsAsync();
if (!result.ok) {
return result;
}
await fetch('https://api.example.com/push-tokens', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer user-session-token',
},
body: JSON.stringify({
userId,
token: result.token,
platform: Platform.OS,
}),
});
return result;
}
Plus tard dans le flux de travail, ce guide est une référence visuelle utile :
Pour les équipes qui construisent des applications avec de nombreuses mises à jour, il est également utile de considérer l'enregistrement du jeton comme partie de l'état opérationnel de l'application, et non seulement comme partie de l'inscription. Cette mentalité s'adapte bien à des flux de travail plus larges Expo app delivery workflowsoù le comportement de l'application peut changer fréquemment et l'état du serveur doit rester synchronisé.
Envoyer des Notifications à Partir de Votre Serveur
Une fois que votre serveur backend dispose d'un token Expo Push valide, envoyer une notification est tout à fait simple. La partie difficile n'est pas la demande elle-même. C'est plutôt déterminer ce qui doit figurer dans le payload et combien de confiance vous placez dans l'état du client.
Ici est un exemple minimal Node-style utilisant __CAPGO_KEEP_0__ fetch:
type ExpoPushMessage = {
to: string;
title: string;
body: string;
sound?: 'default' | null;
data?: Record<string, unknown>;
};
export async function sendExpoPushNotification(token: string) {
const message: ExpoPushMessage = {
to: token,
title: 'New review received',
body: 'Tap to open the order details.',
sound: 'default',
data: {
type: 'new_review',
orderId: 'ord_123',
screen: 'OrderDetails',
},
};
const response = await fetch('https://exp.host/--/api/v2/push/send', {
method: 'POST',
headers: {
Accept: 'application/json',
'Accept-encoding': 'gzip, deflate',
'Content-Type': 'application/json',
},
body: JSON.stringify(message),
});
const result = await response.json();
return result;
}
Ce que chaque champ du payload doit faire
N'abusez pas du payload en y jetant tout. Gardez chaque champ intentionnel.
| Champ | Objectif | Conseils pratiques |
|---|---|---|
to |
Token Expo Push Cible | Vérifiez qu'il appartient au registre actuel du dispositif |
title |
En-tête de notification | Gardez-le court et lisible par les humains |
body |
Texte visible principal | Clarifiez l'action |
sound |
Comportement du son du système | Utilisez avec parcimonie pour les alertes de haute valeur |
data |
Informations métadonnées spécifiques à l'application | Préférez les IDs et les indices de route aux contenus riches |
Le data l'objet est où les flux de travail des produits deviennent utiles. Vous pouvez passer un type et un ID de document, puis laisser l'application récupérer les données les plus récentes lorsque l'utilisateur clique. C'est plus sûr que d'insérer des gros ou des blobs sensibles directement dans le payload.
Conservez les payloads petits et sans intérêt
D'après Guide de Courier pour les notifications Expo, les jetons de notification Expo devraient être traités comme éphémères, les payloads qui dépassent les limites de taille d'environ 4 Ko peut être abandonné, et un modèle fiable est d'envoyer de petits payloads de métadonnées comme { "type": "new_review", "id": 123 } au lieu de grands JSON ou de médias intégrés. Ce conseil correspond à ce qui fonctionne dans les systèmes réels. Les petits payloads échouent moins souvent et vieillissent mieux lorsque les logiques de l'application changent.
Envoyer suffisamment de données pour router l'utilisateur. Récupérer le reste après l'ouverture de l'application.
Les habitudes côté serveur
Une fonction d'envoi de base est suffisante pour les tests. Une fonction de production ajoute généralement quelques responsabilités supplémentaires :
- Conservation des tentatives d'envoi : Enregistrer l'intention de notification avec l'ID de l'utilisateur, le jeton, le type de payload et la date et l'heure.
- Distinction entre la génération de contenu et le transport : Construire la copie du message dans une couche et la demande d'Expo API dans une autre.
- Gestion des retours d'invalidation : Si Expo signale ultérieurement des problèmes
DeviceNotRegisteredMarquez ce token obsolète et arrêtez les tentatives de réessayer aveuglément. - Utilisez un design compatible avec les webhooks : Si votre système émet déjà des événements, dirigez les déclencheurs de notification à travers le même modèle de traitement de webhooks en arrière-plan que vous utilisez ailleurs. Avant de déboguer les écouteurs de client, envoyez d'abord une mise à jour de test manuelle. Si un token reçoit une notification ordinaire avec un petit payload, votre chemin de transport est probablement en bonne santé. Si ce n'est pas le cas, ne commencez pas par modifier la navigation __CAPGO_KEEP_0__. Commencez par valider le token, la forme du payload et l'état de permission. Gestion des Notifications Arrivantes dans Votre Application
Before debugging client listeners, send a manual test push first. If a token receives a plain notification with a tiny payload, your transport path is probably healthy. If not, don’t start by changing navigation code. Start by validating the token, payload shape, and permission state.
Cela signifie gérer deux moments séparés :
la notification arrive tandis que l'application est en arrière-plan
l'utilisateur interagit avec la notification depuis le panneau de notification ou l'écran de verrouillage
- Diagramme de flux illustrant le cycle de vie des notifications push pour les applications mobiles dans les états d'avant-plan et d'arrière-plan.
- Handling Incoming Notifications in Your App

Les événements de la réception de l'avant-plan et de la réponse de l'utilisateur sont différents
Une configuration fiable comprend généralement les deux écouteurs :
import { useEffect } from 'react';
import * as Notifications from 'expo-notifications';
export function useNotificationObservers(
onForegroundMessage: (notification: Notifications.Notification) => void,
onNotificationTap: (response: Notifications.NotificationResponse) => void
) {
useEffect(() => {
const receivedSub = Notifications.addNotificationReceivedListener(
(notification) => {
onForegroundMessage(notification);
}
);
const responseSub = Notifications.addNotificationResponseReceivedListener(
(response) => {
onNotificationTap(response);
}
);
return () => {
receivedSub.remove();
responseSub.remove();
};
}, [onForegroundMessage, onNotificationTap]);
}
addNotificationReceivedListener Exécuté lorsque l'application est active. addNotificationResponseReceivedListener Exécuté lorsque l'utilisateur clique sur une notification délivrée. N'y pensez pas mentalement. Ils servent des chemins UX différents.
Lisez le payload de données et naviguez intentionnellement
Ceci est un modèle pratique pour la gestion des appuis :
type NotificationData = {
type?: string;
orderId?: string;
screen?: string;
};
export function handleNotificationTap(
response: Notifications.NotificationResponse,
navigation: any
) {
const data =
response.notification.request.content.data as NotificationData;
if (data.screen === 'OrderDetails' && data.orderId) {
navigation.navigate('OrderDetails', { orderId: data.orderId });
return;
}
if (data.type === 'new_review') {
navigation.navigate('Inbox');
return;
}
navigation.navigate('Home');
}
Ce modèle reste résilient car le payload contient des indices de routage, pas des documents complets. Si l'ordre a changé depuis que la notification a été envoyée, l'application peut récupérer l'état serveur actuel après la navigation.
Une notification tapée devrait conduire à un seul destinataire évident. Si votre chemin de secours est vague, les utilisateurs le remarquent immédiatement.
Le comportement en avant-plan doit correspondre au contexte de l'utilisateur
Lorsque l'application est déjà ouverte, afficher un avertissement de style système sans réfléchir peut paraître maladroit. Parfois, la bonne action est une bannière en application, une mise à jour de badge ou une mise à jour silencieuse. Une page de boîte à lettres de support ne nécessite peut-être pas d'avertissement visible lorsque l'utilisateur lit déjà cette conversation.
C'est pourquoi votre écouteur en avant-plan doit se brancher en fonction de la route et du type de notification. Par exemple :
- Écran de messagerie ouvert : ajoutez le message et évitez un bandeau redondant
- Ouverture du tableau de bord : affichez un toast léger dans l'application
- Événement compte critique : donnez une meilleure prise en charge graphique
Une approche simple ressemble à ceci :
export function handleForegroundNotification(
notification: Notifications.Notification,
currentRouteName: string
) {
const data = notification.request.content.data as { type?: string };
if (currentRouteName === 'ChatThread' && data.type === 'new_message') {
// refresh local thread state
return;
}
// otherwise show your own in-app UI or update badges
}
Si votre application ne distingue pas ces contextes, les utilisateurs ressentiront une fatigue des notifications plus rapidement, même si la livraison est correcte sur le plan technique.
Meilleures pratiques et pièges courants en production
La plupart des configurations de notification push Expo ne fonctionnent pas car les équipes supposent que le jeton est permanent, que les payloads peuvent transporter n'importe quoi et que les mises à jour de l'application n'affecteront pas la logique des notifications.
Cette supposition ne résiste pas en production.

Les jetons sont éphémères, pas des enregistrements d'identité
Un jeton de Push Expo est mieux traité comme un bail, et non comme un identifiant de périphérique à vie. Les jetons peuvent tourner après un réinstall, des changements d'OS ou d'autres événements de cycle de vie. Si un jeton revient finalement DeviceNotRegistered votre serveur backend devrait cesser de le traiter comme actif.
Un modèle backend pratique stocke :
- ID d'utilisateur
- plateforme
- informations de métadonnées liées à l'installation
- jeton actuel
- timestamp de dernière vue
- état tel que actif, obsolète ou révoqué
N'installez pas un seul champ de jeton sur la table utilisateur et appelez-le terminé. Les utilisateurs ont plusieurs appareils, et les appareils changent d'état.
La stratégie de mise à jour compte plus que la plupart des tutoriels ne l'admettent
La guidance officielle de l'écosystème laisse un véritable écart opérationnel ici. Le contenu existant des notifications de Push Expo ne parle souvent pas de la manière de maintenir la validité des jetons à travers Les cycles de révision de l'App Store et les mises à jour OTA, ce qui est particulièrement important pour les équipes qui livrent des changements en direct car la fiabilité de la mise à jour dépend de l'état actuel du jeton et de la synchronisation back-end, comme le note la documentation des notifications d'Expo.
Cela affecte la façon dont vous conçez les déclencheurs de mise à jour. Les bons moments pour réconcilier l'état du jeton incluent :
- Lancement de l'application après une mise à jour
- Connexion de l'utilisateur
- Changement des paramètres de permission
- Rotation des informations d'identification dans votre processus de publication
- Flux de récupération après des tickets de support liés à la mise à jour
La sécurité et la conformité ne doivent pas se trouver à la fin du sprint
Un grand nombre de tutoriels d'Expo se concentrent sur les mécaniques et ignorent le risque opérationnel. C'est acceptable pour les applications de loisirs. Ce n'est pas acceptable pour les produits de commerce réglementé, adjacents à la santé ou à la finance.
La discussion d'Expo de Courier sur les lacunes des notifications s'adresse aux entreprises met en évidence un manque de conseils pratiques autour de la gestion du consentement, des traçages d'audit et de la minimisation de l'exposition des données sensibles dans les payloads.
- Ne mettez pas de données sensibles à caractère commercial dans le texte ou les métadonnées des notifications.
- Enregistrez les modifications du consentement côté serveur.
- Enregistrez les notifications envoyées à quel token.
- Utilisez les IDs dans les payloads et récupérez le contenu protégé après l'ouverture de l'application.
Pour les équipes alignant les opérations de mise en production avec les pratiques de sécurité plus larges et __CAPGO_KEEP_0__ de l'application Store app store compliance and API security practicesLes notifications push sont des messages destinés aux utilisateurs, mais elles constituent également un problème de systèmes distribués. Traitez-les avec la même attention que vous appliquez à l'état d'authentification et aux événements de paiement.
Ce qui fonctionne généralement et ce qui fonctionne généralement
Ce qui fonctionne généralement
| Ce qui ne fonctionne généralement pas | Ce qui fonctionne généralement |
|---|---|
| Demander la permission après une explication claire de la valeur | Demander confirmation à la première frame |
| Tester sur des appareils réels | Confier la confiance à l'enregistrement du simulateur |
| Stocker les jetons avec le contexte de l'appareil | Un jeton par enregistrement d'utilisateur |
| Envoyer des payloads de métadonnées de petite taille | Intégrer de grandes ou sensibles blobs |
| Gérer les événements de tap et de foreground séparément | Supposer que toutes les notifications suivent un chemin |
| Expirez les jetons périmés avec agressivité | Réessayer les jetons morts pendant tout le temps |
Une mise en place solide d'Expo push n'est pas compliquée. C'est une discipline.
Si votre équipe livre fréquemment des modifications de logique d'application et a besoin d'un contrôle plus serré sur le comportement de la mise en production, les retours en arrière, et la visibilité de la livraison, Capgo vaut la peine d'y jeter un coup d'œil. Il aide les équipes mobiles à envoyer des mises à jour rapidement sans attendre la revue des magasins, ce qui est particulièrement utile lorsque les flux de notification, la logique de routage ou les corrections côté client doivent atteindre les utilisateurs rapidement.