Probablemente estás en el punto en el que la aplicación funciona, los usuarios han iniciado sesión y el producto ahora quiere flujos de reenganche que se sientan nativos. Recordatorios de la cesta. Prompts de revisión. Alertas de nuevos mensajes. Anuncios de lanzamiento. La primera intuición es a menudo simplemente conectar la notificación, luego una semana más tarde estás depurando por qué un dispositivo recibe alertas, el simulador parece registrarse bien y nadie puede explicar por qué los toques no abren la pantalla correcta.
Eso es donde las notificaciones de Expo son a la vez sencillas y sorprendentemente frágiles.
Expo proporciona a los equipos de React Native una capa práctica sobre APNs y FCM, lo que es exactamente por qué tantos equipos la utilizan. Pero la brecha entre una demostración y una implementación lista para producción es real. El ciclo de vida del token, el momento de la autorización, la configuración de los escuchadores, el diseño del payload y la limpieza del backend importan. Si también estás enviando cambios frecuentes en la lógica de la aplicación, la necesidad de disciplina operativa se vuelve aún más aguda, especialmente si tu trabajo de retención de usuarios depende de mensajería confiable y velocidad de lanzamiento. Eso es el mismo preocupación más amplia detrás mejora la retención de usuarios de aplicaciones móviles: la entrega solo es útil si la experiencia del usuario alrededor de ella es predecible.
Contenido de la Tabla
- La Fundación para Engañar a los Usuarios con Notificaciones de Expo
- Configuración y configuración del proyecto inicial
- Solicitar permiso y capturar tokens de notificación
- Enviar notificaciones desde su servidor
- Manipulación de Notificaciones de Entrada en tu Aplicación
- Prácticas recomendadas de producción y trampas comunes
La base para involucrar a los usuarios con Notificaciones de Expo Push
Un Configuración de notificación de Expo setup es atractivo por una razón sobre todas las demás. Elimina una gran cantidad de complejidad de mensajería nativa que los equipos no quieren asumir el día uno. En lugar de construir tuberías de APNs y FCM directas primero, puede trabajar con la puerta de enlace de Expo y centrarse en el comportamiento del producto, la ruta, la UX de permisos y la lógica de mensajes de backend.
Esa abstracción no hace que la notificación sea ‘menos real’. Solo cambia dónde va su esfuerzo de ingeniería.
El servicio también es lo suficientemente rápido como para que el rendimiento no sea la primera cosa a la que preocuparse. Desde el 14 de marzo de 2023 hasta el 12 de junio de 2023, la notificación de Expo API mostró un 42 milisegundos de tiempo de respuesta mediano, 273 milisegundos de latencia p99, y un tasa diaria de errores promedio del 0,17% en millones de mensajes diariosde acuerdo a Knock’s Expo push API benchmark analysisEsto debería tranquilizar a cualquier equipo que se pregunte si Expo es solo adecuado para prototipos.
¿Qué es lo que realmente abstrae Expo?
Cuando los equipos dicen "notificación de Expo", a menudo se refieren a varios asuntos separados empaquetados juntos.
- Ruteo del proveedor: Expo envía mensajes a APNs para iOS y FCM para Android.
- Formato de token: Su servidor almacena y envía un Expo Push Token en lugar de gestionar el manejo de tokens específicos de plataforma primero.
- Solicitud de contrato: Envías un payload a Expo's push API en lugar de integrar directamente las APIs de proveedores nativos.
Eso es útil, pero también crea una confusión común. Los equipos a veces asumen que Expo es responsable de todos los problemas de entrega. En la práctica, muchos fallos provienen de la aplicación code, tokens caducados, payloads malformados o flujo de permisos deficiente.
Regla práctica: Trate a Expo como una capa de transporte confiable, no como un sustituto de un diseño cliente y servidor sólido.
¿Qué significa realmente la preparación para producción?
Una demostración funcional solo demuestra que un dispositivo aceptó un paquete una vez. La preparación para producción significa algo más.
| Concern | Mentalidad de producción | Mentalidad de producción |
|---|---|---|
| Permissions | Pregúntale inmediatamente | Ask in context, after user value is clear |
| Tokens | Guardar una vez | Actualizar, deduplicar, expirar y reconciliar |
| Payloads | Meter todo en data |
Mantener payloads pequeños y orientados a la acción |
| Comportamiento de la aplicación | Mostrar una alerta | Navegar correctamente y manejar el estado de primer plano |
| Operaciones | Pruebas manuales | Recibos, limpieza, registros y manejo de incidentes |
La diferencia entre “enviar notificaciones” y “notificaciones apoyan un flujo de trabajo de producto real”.
Configuración y configuración inicial del proyecto
Un gran dolor de cabeza con las notificaciones de Expo comienza antes de la primera solicitud de permiso. Si la configuración de su proyecto es descuidada, el cliente code puede parecer correcto mientras la aplicación sigue comportándose de manera inconsistente en diferentes compilaciones.

Comience con las bibliotecas instaladas correctamente y un entorno de desarrollo que coincida con su ruta de compilación. Si está trabajando más allá de Expo Go, ayuda a alinear su flujo de trabajo local con una custom Expo development client setupporque el comportamiento de las notificaciones a menudo necesita ser validado en un build que refleje más de cerca la producción que una rápida ejecución de pruebas.
Instale los paquetes de notificación
Al menos, generalmente necesitará:
expo-notificationspara solicitudes de permiso, recuperación de tokens, escuchas y presentación de notificaciones.expo-deviceporque debe proteger la recuperación de tokens conDevice.isDevice.
Los comandos de instalación típicos dependen de su administrador de paquetes, pero la clave es la alineación de versiones con su Expo SDK. No mezcle versiones de paquetes arbitrarias. Deje que Expo resuelva las compatibles.
Agregar configuración a nivel de proyecto
mantenga su configuración explícita. Un mínimo app.json o app.config.js debería reflejar el hecho de que las notificaciones forman parte del contrato de tu aplicación, no un añadido.
{
"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"
}
}
}
}
Unos pocos detalles importan aquí.
- Unos detalles importan aquí: Los identificadores de paquetes y nombres de paquetes
- El plugin de notificaciones garantiza que el proyecto nativo obtiene la configuración necesaria durante la compilación.
- El ID del proyecto de EAS importa el token correcto para que la aplicación esté asociada con el proyecto Expo adecuado.
Establecer un manejador de notificaciones temprano
Define el comportamiento de notificación cerca del inicio de la aplicación para que el comportamiento en primer plano sea predecible.
import * as Notifications from 'expo-notifications';
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: true,
}),
});
Las equipos deciden si las notificaciones de primer plano deben mostrar una alerta, reproducir un sonido o afectar las insignias. El comportamiento exacto depende de su producto. Una aplicación de chat y una aplicación de pago no harán las mismas elecciones.
Si no definen el comportamiento de primer plano de manera intencional, su equipo terminará debatiendo sobre notificaciones 'faltantes' que se recibieron pero nunca se presentaron de la manera que el producto esperaba.
Android necesita configuración de canal
Los canales de notificación de Android no son opcionales en la práctica. Si los omites, tus alertas pueden parecer inconsistentes o no cumplir con las expectativas del usuario.
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,
});
}
Configúrelo durante la inicialización de la aplicación. Luego mantenga los IDs de canal estable. Cambiarlos con frecuencia complica la comprensión del comportamiento de las notificaciones más adelante.
Solicitar permiso y capturar tokens de empuje
Esto es la parte que muchos equipos copian de un snippet, luego eventualmente se arrepienten.
Las solicitudes de permiso necesitan tener un momento, conciencia de plataforma y manejo asincrónico disciplinado. La captura de tokens debe ocurrir solo en un dispositivo físico, solo después de que se resuelvan las permisiones, y solo si están listos para almacenar el resultado en su servidor de inmediato.

La función del cliente que debe ser su línea base
Use a function like this as your starting point:
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 };
}
La orden importa. Primero, verifica el tipo de dispositivo, configura el comportamiento del canal de Android, resuelve las permisos, valida la configuración del proyecto, luego solicita el token de Expo.
Preguntas Device.isDevice No es opcional
Esta es una de las pocas equivocaciones que crea mucho ruido mientras parece inofensiva. Los equipos expertos solo solicitan permiso condicionalmente cuando Device.isDevice es verdadero, y un error común es omitir ese guardián, lo que lleva a los desarrolladores a enviar notificaciones a tokens de simulador inválidos y culpar a Expo cuando el problema es realmente la configuración de la aplicación, como se describe en Implementación de notificaciones de Expo de Eagerworks.
Por eso, el control se encuentra en la parte superior de la función. No lo oculte detrás de un helper. Hágalo obvio.
Los resultados de simulador son útiles para la prueba de UI. No son confiables para validar la inscripción del token de notificación.
Pide permiso en el momento adecuado
contexto: Página/área: Sitio web de marketing de Capgo. Rol: Etiqueta de botón o enlace de llamada a la acción. Visto en: componente AskAiSection.astro. Clave de mensaje `ask_ai_button` (Pide a la inteligencia artificial).
A una buena implementación le sigue este flujo:
- El usuario alcanza un límite de características significativo.
- La aplicación explica el valor de las notificaciones en su propio UI.
- La aplicación solicita permiso del sistema.
- La aplicación almacena el token en el backend de inmediato si se concede el permiso.
Este último paso es donde muchas aplicaciones fallan. Recuperan el token, lo registran localmente y posponen la inscripción en el backend. Más tarde, el soporte no puede determinar qué dispositivo tenía qué token en qué momento.
Aquí hay un ejemplo simple de almacenar el token después de la inscripción:
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;
}
Más adelante en el flujo de trabajo, esta guía es una útil referencia visual:
For teams building release-heavy apps, it also helps to think of token registration as part of the app’s operational state, not just part of onboarding. That mindset fits well with broader Flujos de trabajo de entrega de aplicaciones de Expodonde el comportamiento de la app cambia con frecuencia y el estado del backend necesita mantenerse sincronizado.
Enviar Notificaciones Desde Su Servidor
Una vez que su servidor tenga un token de Expo Push válido, enviar una notificación es sencillo. La parte difícil no es la solicitud en sí. Es decidir qué pertenece al payload y cuánta confianza le otorga al estado del cliente.
Aquí hay un ejemplo mínimo de estilo Node usando 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;
}
¿Qué debe hacer cada campo del payload?
No trate el payload como un contenedor de basura. Mantenga cada campo intencional.
| Campo | Propósito | Consejo práctico |
|---|---|---|
to |
Destino del token de Expo Push | Validar que pertenece al registro del dispositivo actual |
title |
Título de la notificación | Manténgalo corto y legible |
body |
Texto visible principal | Haz que la acción sea clara |
sound |
Comportamiento de sonido del sistema | Usa con moderación para alertas de alto valor |
data |
Metadatos específicos de la aplicación | Preferir IDs y pistas de ruta sobre contenido rico |
El data objeto es donde los flujos de trabajo de productos se vuelven útiles. Puedes pasar un tipo y un ID de registro, y luego dejar que la aplicación obtenga los datos más recientes cuando el usuario toque. Eso es más seguro que insertar grandes o sensibles blobs directamente en el payload.
Mantén los payloads pequeños y aburridos
According to la guía de Courier sobre notificaciones de Expo, los tokens de notificación de Expo deben considerarse como efímeros, payloads que exceden los límites de tamaño de aproximadamente 4 KB puede ser descartado, y un patrón confiable es enviar pequeños payloads de metadatos como { "type": "new_review", "id": 123 } en lugar de grandes JSON o medios inline. Ese consejo coincide con lo que funciona en sistemas reales. Los payloads pequeños fallan menos a menudo y envejecen mejor cuando cambian las lógicas de la aplicación.
Envíe suficiente datos para dirigir al usuario. Recupere el resto después de que la aplicación se abra.
Hábitos útiles del lado del servidor
Una función básica de envío es suficiente para la prueba. Una función de producción suele agregar algunas responsabilidades adicionales:
- Persistir intentos de envío: Guardar la intención de notificación con ID de usuario, token, tipo de carga y fecha de timestamp.
- Separar la generación de contenido del transporte: Crear copia de mensaje en un nivel y la solicitud de Expo API en otro.
- Manejar feedback de invalidación: Si Expo informa más tarde
DeviceNotRegistered, marca ese token como obsoleto y detén los intentos de reintento. - Utilice un diseño amigable con webhooks: Si su sistema ya emite eventos, dirija los disparadores de notificaciones a través del mismo tipo de patrón de procesamiento de webhook de backend usas en otros lugares.
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.
Manejo de Notificaciones de Entrada en Tu Aplicación
La entrega es solo la mitad de la característica. La aplicación debe hacer algo coherente cuando llega la notificación y cuando el usuario la toca.
La notificación llega mientras la aplicación está abierta
- La notificación llega mientras la aplicación está abierta.
- El usuario interactúa con la notificación desde la bandeja de sistema o pantalla de bloqueo.

La recepción en primer plano y la respuesta del usuario son eventos diferentes
A una configuración confiable suele incluir ambos escuchadores:
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 se ejecuta cuando la aplicación está activa. addNotificationResponseReceivedListener se ejecuta cuando el usuario hace clic en una notificación entregada. No los combinen mentalmente. Sirven diferentes rutas de UX.
Lee el payload de datos y navega intencionalmente
Aquí está un patrón práctico para el manejo de toques:
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');
}
Este patrón permanece resistente porque el payload contiene pistas de enrutamiento, no documentos completos. Si el orden ha cambiado desde que se envió la notificación, la aplicación puede recuperar el estado del servidor actual después de la navegación.
Una notificación pulsada debe llevar a un destino obvio. Si su ruta de fallback es vaga, los usuarios lo notan inmediatamente.
Comportamiento de fondo debe coincidir con el contexto del usuario
Cuando la aplicación ya está abierta, mostrar una alerta de estilo de sistema de manera ciega puede sentirse torpe. A veces el movimiento correcto es una bandera de aplicación, actualización de insignia o refresco silencioso. Una pantalla de bandeja de entrada de soporte puede no necesitar una alerta visible cuando el usuario ya está leyendo esa conversación.
Su foreground listener debe ramificarse según la ruta y el tipo de notificación. Por ejemplo:
- La pantalla de chat está abierta: agrega el mensaje y evita una bandera redundante
- Panel de control abierto: Mostrar un toast ligero en la aplicación
- Evento crítico de la cuenta: tratamiento de interfaz de usuario más robusto
Un enfoque simple parece así:
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 su aplicación no distingue estos contextos, los usuarios sentirán cansancio de notificaciones más rápido, incluso si la entrega es técnicamente correcta.
Prácticas recomendadas y trampas comunes en producción
La mayoría de las configuraciones de notificaciones de Expo rotas no fallan porque Expo es demasiado limitado. Fallan porque los equipos asumen que el token es permanente, los payloads pueden llevar cualquier cosa y las actualizaciones de la aplicación no afectarán la lógica de las notificaciones.
Esa asunción no sobrevive a la producción.

Los tokens son efímeros, no registros de identidad
Un token de notificación de Expo se debe tratar como un alquiler, no como un dispositivo identificador de vida entera. Los tokens pueden rotar después de reinstalar, cambios de sistema operativo o otros eventos de ciclo de vida. Si un token eventualmente regresa DeviceNotRegistered, su servidor debe dejar de tratarlo como activo.
Un modelo de backend práctico almacena:
- ID de usuario
- plataforma
- instalar metadatos de ámbito
- token actual
- timestamp de última visualización
- estado como activo, caducado o revocado
No almacenes un campo de token en la tabla de usuarios y listo. Los usuarios tienen varios dispositivos y los dispositivos cambian de estado.
La estrategia de refresco importa más que la mayoría de los tutoriales admiten
La guía oficial del ecosistema deja un verdadero vacío operativo aquí. El contenido de notificaciones de Expo existente a menudo no explica cómo mantener la validez del token a través de Ciclos de revisión de la App Store y actualizaciones OTAEs especialmente importante para equipos que envían cambios en vivo, ya que la confiabilidad de la notificación push depende del estado actual del token y la sincronización del servidor, como se menciona en documentación de notificaciones de Expo.
Que afecta cómo diseñan los desencadenantes de refresco. Buenos momentos para reconciliar el estado del token incluyen:
- Lanzamiento de la aplicación después de una actualización
- Inicio de sesión del usuario
- Cambio de configuración de permisos
- Rotación de credenciales en su proceso de lanzamiento
- Recovery flows after push-related support tickets
La seguridad y la conformidad no deben estar al final del sprint
Muchas tutoriales de Expo se centran en mecánicas y omiten el riesgo operativo. Eso está bien para aplicaciones de hobby. No está bien para productos comerciales relacionados con la atención médica, fintech o comercio regulado.
Discusión de Expo de notificaciones de Courier enfocada en empresas Destaca una falta de orientación práctica sobre el registro de consentimiento, registros de auditoría y minimización de exposición de paquetes sensibles. La toma de ingeniería directa es simple:
- No ponga datos de negocio sensibles en el texto o metadatos de la notificación.
- Actualiza cambios de consentimiento en el servidor.
- Registre qué intención de notificación se envió a qué token.
- Utilice IDs en los payloads y recupere contenido protegido después de abrir la aplicación.
For teams aligning release operations with broader compliance con la tienda de aplicaciones y prácticas de seguridad APIpush debe incluirse en el mismo conjunto de revisión que auth, analytics y el registro de eventos de backend.
Push notifications are user-facing messages, but they’re also a distributed systems problem. Treat them with the same care you apply to auth state and payment events.
¿Qué funciona normalmente y qué normalmente se rompe
| Se rompe normalmente | Generalmente se rompe |
|---|---|
| Solicitar permiso después de una explicación clara del valor | Solicitando en la primera frame |
| Probando en dispositivos reales | Confirmando la inscripción del simulador |
| Almacenando tokens con contexto de dispositivo | Un token por registro de usuario |
| Enviando payloads de metadatos pequeños | Incorporando grandes o sensibles blobs |
| Manejando eventos de tap y de fondo por separado | Suponiendo que todas las notificaciones siguen un camino |
| Venciendo tokens caducados de manera agresiva | Reintentando tokens muertos para siempre |
Una configuración de notificaciones de Expo sólida no es complicada. Es disciplinada.
Si su equipo envía cambios lógicos de la aplicación con frecuencia y necesita un control más estricto sobre el comportamiento de la liberación, los reenvíos y la visibilidad de la entrega. Capgo is worth a look. It helps mobile teams push updates quickly without waiting on store review, which is especially useful when notification flows, routing logic, or client-side fixes need to reach users fast.