Notifiche Push in React Native nel 2026: Expo Notifications, FCM e APNs

Guida pratica alle notifiche push in React Native nel 2026: quando usare Expo Notifications, quando passare a FCM v1 e APNs nativo, permessi Android 13+, canali, deep linking e debug.

Notifiche Push React Native: Guida 2026

Aggiornato: 10 settembre 2026

Le notifiche push in React Native nel 2026 si configurano nel modo più rapido con Expo Notifications, che maschera FCM su Android e APNs su iOS dietro un unico token e un unico endpoint. Se ti servono payload personalizzati, notifiche silenziose, service extension o payload superiori a 4 KB, però, conviene bypassare il servizio Expo e parlare direttamente con Firebase Cloud Messaging (HTTP v1) e Apple Push Notification service. Ho fatto entrambe le scelte in produzione, e onestamente ognuna ha senso in contesti diversi. Qui trovi come, quando e perché sceglierne una.

  • Expo Notifications SDK 53 è il percorso più rapido nel 2026: un solo Expo Push Token per iOS e Android e un endpoint HTTP unificato.
  • FCM HTTP v1 è ora obbligatorio: la vecchia API "legacy" è stata dismessa da Google a giugno 2024 e non è più utilizzabile.
  • Android 13+ richiede il permesso runtime POST_NOTIFICATIONS, e su Android 8+ ogni notifica deve appartenere a un canale creato dall'app.
  • iOS impone chiavi .p8 APNs per l'invio server-to-server e un Notification Service Extension per notifiche rich con immagini o video.
  • Il deep linking dalle notifiche si gestisce con Expo Router v6 leggendo notification.request.content.data.url nel listener globale.
  • La maggior parte dei "non ricevo le notifiche" si risolve controllando permessi, canali Android, e ambiente APNs (sandbox vs production).

Come funzionano le notifiche push in React Native

Una notifica push in React Native è un messaggio che il sistema operativo consegna al dispositivo anche quando l'app è chiusa. Il flusso è sempre lo stesso, indipendentemente dallo stack che scegli: l'app registra un token univoco per dispositivo con il push service della piattaforma, invia quel token al tuo backend, e il backend lo usa per inviare payload firmati al gateway push (APNs per iOS, FCM per Android). Il gateway consegna il payload al sistema operativo del dispositivo, che poi lo mostra o lo passa all'app se è in foreground.

La differenza sostanziale nel 2026 è chi gestisce l'astrazione multipiattaforma. Con Expo Notifications, il servizio Expo riceve un solo token (l'Expo Push Token) e internamente lo instrada al gateway giusto, gestendo credenziali, retry e rate limiting. Con l'approccio nativo, la tua app registra due token diversi (uno FCM e uno APNs) e il backend deve conoscere entrambe le API. Questa scelta ha ricadute su cosa puoi mettere nel payload, su come gestisci le notifiche silenziose, e su quanto lavoro fai in fase di build.

Expo Notifications vs setup nativo con FCM e APNs

La domanda più frequente che ricevo è "devo per forza usare Firebase per le notifiche in React Native?". La risposta breve è: no, ma dipende. Se stai già usando Expo (che oggi consiglio a chiunque non abbia vincoli forti sulla New Architecture custom), Expo Notifications ti evita di scrivere una riga di codice Firebase su Android e di generare manualmente token APNs su iOS. Per una lettura più profonda su come questo si incastra con il resto della toolchain moderna, ho scritto una guida alla Nuova Architettura di React Native che spiega perché nel 2026 non ha più senso partire dalla CLI vanilla.

AspettoExpo NotificationsFCM + APNs nativo
Setup iniziale~30 minuti4–8 ore (certificati, plist, google-services.json)
Token per dispositivo1 Expo Push Token2 token (FCM + APNs)
Dimensione payload iOS4 KB (limite APNs)4 KB (con extension: fino a 4 KB + attachment)
Notifiche silenziose iOSSupportate via _contentAvailableControllo completo su content-available
Rich notifications (immagini)Sì, ma con limitazioniPiena, con Notification Service Extension
Rate limiting~600 msg/sec per progettoLimiti FCM/APNs di piattaforma
CostoGratisGratis (FCM e APNs)
Vendor lock-inServizio Expo obbligatorioNessuno

La mia regola pratica: parti con Expo Notifications. Il 90% delle app non ha bisogno di rich notification con Notification Service Extension o di payload custom che eccedano i limiti Expo. Quando quel giorno arriva, di solito perché il team marketing vuole notifiche con immagini dinamiche, o perché il security team chiede payload cifrati end-to-end, puoi migrare al setup nativo senza cambiare il resto dell'app: il token per dispositivo cambia, ma la logica di consegna lato backend è isolata.

Configurare Expo Notifications passo per passo

Ecco il setup completo per un'app Expo SDK 53 nel 2026. Assumo che tu abbia già un progetto EAS collegato. Se non ce l'hai, la mia guida a EAS Build e Submit copre tutta la configurazione dei certificati e dei profili di provisioning.

Installa il pacchetto e configura app.json:

npx expo install expo-notifications expo-device

# app.json
{
  "expo": {
    "plugins": [
      [
        "expo-notifications",
        {
          "icon": "./assets/notification-icon.png",
          "color": "#0EA5E9",
          "defaultChannel": "default",
          "sounds": ["./assets/notification-sound.wav"]
        }
      ]
    ],
    "ios": {
      "bundleIdentifier": "com.acme.app",
      "infoPlist": {
        "UIBackgroundModes": ["remote-notification"]
      }
    },
    "android": {
      "package": "com.acme.app",
      "googleServicesFile": "./google-services.json"
    }
  }
}

Su Android è ancora obbligatorio un progetto Firebase, anche se usi Expo Notifications: FCM è il trasporto sottostante. Scarica google-services.json dalla console Firebase e mettilo alla radice del progetto. Su iOS non serve altro, perché EAS Build si occupa delle credenziali APNs automaticamente al primo eas credentials.

Registra il device e ottieni il push token:

import * as Notifications from 'expo-notifications';
import * as Device from 'expo-device';
import { Platform } from 'react-native';

Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowBanner: true,
    shouldShowList: true,
    shouldPlaySound: true,
    shouldSetBadge: true,
  }),
});

export async function registerForPushNotifications(): Promise<string | null> {
  if (!Device.isDevice) {
    console.warn('Le notifiche push funzionano solo su dispositivo fisico');
    return null;
  }

  if (Platform.OS === 'android') {
    await Notifications.setNotificationChannelAsync('default', {
      name: 'Notifiche generali',
      importance: Notifications.AndroidImportance.DEFAULT,
      vibrationPattern: [0, 250, 250, 250],
      lightColor: '#0EA5E9',
    });
  }

  const { status: existing } = await Notifications.getPermissionsAsync();
  let finalStatus = existing;

  if (existing !== 'granted') {
    const { status } = await Notifications.requestPermissionsAsync();
    finalStatus = status;
  }

  if (finalStatus !== 'granted') return null;

  const projectId = process.env.EXPO_PUBLIC_PROJECT_ID;
  const token = await Notifications.getExpoPushTokenAsync({ projectId });
  return token.data; // es. ExponentPushToken[xxxxxxxxxxxxxx]
}

Gestire i permessi su iOS e Android 13+

I permessi sono il punto dove più app inciampano. Su iOS il modello è semplice: alla prima chiamata a requestPermissionsAsync il sistema mostra il prompt; se l'utente rifiuta, non puoi più chiedere e devi rimandarlo alle impostazioni. Su Android è più sottile. Fino ad Android 12 le notifiche erano concesse di default; da Android 13 (API 33) serve il permesso runtime POST_NOTIFICATIONS, e il sistema può concederlo automaticamente solo se l'utente non ha mai bloccato l'app.

Il pattern che uso in produzione è a due stadi. Il primo stadio è un "pre-prompt": una schermata di onboarding in cui spiego perché servono le notifiche prima di chiedere i permessi al sistema. Questo alza il tasso di opt-in dal 40–50% tipico al 70–80%, perché una volta che l'utente rifiuta il prompt di sistema non puoi più riprovare senza mandarlo nelle impostazioni. Il secondo stadio è la chiamata effettiva a requestPermissionsAsync, che triggera il dialog di sistema.

import * as Notifications from 'expo-notifications';
import { Linking, Platform } from 'react-native';

export async function ensureNotificationPermission() {
  const { status, canAskAgain } = await Notifications.getPermissionsAsync();

  if (status === 'granted') return true;

  if (canAskAgain) {
    const result = await Notifications.requestPermissionsAsync({
      ios: {
        allowAlert: true,
        allowBadge: true,
        allowSound: true,
        allowProvisional: false,
      },
    });
    return result.status === 'granted';
  }

  // L'utente ha rifiutato in modo permanente: rimandalo alle impostazioni.
  Linking.openSettings();
  return false;
}

Canali di notifica su Android e categorie iOS

Da Android 8 (Oreo) ogni notifica deve essere associata a un canale, altrimenti il sistema la scarta silenziosamente. Un canale è un raggruppamento di notifiche che l'utente può disattivare separatamente dalle impostazioni: "promo", "messaggi", "sistema". La regola nel 2026 è: crea i canali all'avvio dell'app, non alla prima notifica. Cambiare l'importanza di un canale esistente non ha effetto retroattivo, quindi definisci i canali corretti da subito.

import * as Notifications from 'expo-notifications';

export async function setupAndroidChannels() {
  await Notifications.setNotificationChannelAsync('messages', {
    name: 'Messaggi',
    importance: Notifications.AndroidImportance.HIGH,
    sound: 'default',
    vibrationPattern: [0, 250, 250, 250],
    lightColor: '#0EA5E9',
    lockscreenVisibility: Notifications.AndroidNotificationVisibility.PUBLIC,
  });

  await Notifications.setNotificationChannelAsync('promotions', {
    name: 'Promozioni',
    importance: Notifications.AndroidImportance.LOW,
    sound: null,
  });

  await Notifications.setNotificationChannelAsync('system', {
    name: 'Sistema',
    importance: Notifications.AndroidImportance.MIN,
    showBadge: false,
  });
}

Su iOS l'equivalente sono le notification categories, che servono principalmente ad aggiungere azioni interattive (bottoni di risposta rapida). Le categorie si registrano una volta all'avvio con Notifications.setNotificationCategoryAsync. A differenza dei canali Android, non permettono all'utente di disabilitare selettivamente una categoria: su iOS l'utente può solo silenziare tutta l'app.

Inviare notifiche dal server: Expo Push API e FCM v1

Con Expo Notifications, inviare notifiche dal backend è una POST HTTP. Il servizio Expo si occupa di firmare le richieste FCM v1 e APNs per te. Il payload accetta fino a 100 messaggi per chiamata, e Expo raccomanda un rate massimo di 600 messaggi/secondo per progetto. Per la documentazione ufficiale delle opzioni disponibili, consulta la Expo Push API reference.

// Node.js / TypeScript, server-side
async function sendPushNotifications(tokens: string[], title: string, body: string, data: Record<string, unknown>) {
  const messages = tokens.map((to) => ({
    to,
    sound: 'default',
    title,
    body,
    data,
    channelId: 'messages', // solo Android
    priority: 'high',
  }));

  const chunks: typeof messages[] = [];
  for (let i = 0; i < messages.length; i += 100) {
    chunks.push(messages.slice(i, i + 100));
  }

  const results = [];
  for (const chunk of chunks) {
    const res = await fetch('https://exp.host/--/api/v2/push/send', {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(chunk),
    });
    results.push(await res.json());
  }
  return results;
}

Se scegli FCM v1 nativo, il flusso è più verboso ma dà controllo totale. Devi generare un OAuth 2.0 access token da un service account JSON e inviare il payload al Firebase Cloud Messaging HTTP v1 endpoint. Ricorda che la vecchia FCM legacy API è stata definitivamente dismessa da Google nel giugno 2024: se trovi tutorial che usano il server key statico, sono obsoleti e non funzionano più. Per APNs, il pattern moderno è usare token JWT firmati con una chiave .p8 generata dall'Apple Developer Portal, che scadono ogni ora e vanno rigenerati. Consulta la documentazione Apple Push Notification service per l'endpoint corretto.

Deep linking dalle notifiche push

Aprire uno screen specifico quando l'utente tocca una notifica è un requisito quasi universale. Il pattern in Expo Router v6 è mettere l'URL di destinazione dentro data.url del payload e leggerlo nel listener globale. Non tentare di navigare da dentro il componente che ha ricevuto la notifica: il timing è fragile e su cold start il router potrebbe non essere ancora montato. Se non hai familiarità con la nuova navigazione file-based, la mia guida a Expo Router v6 spiega come funzionano le route e i deep link.

import { useEffect } from 'react';
import * as Notifications from 'expo-notifications';
import { router } from 'expo-router';

export function useNotificationDeepLinks() {
  useEffect(() => {
    // Notifica toccata quando l'app era chiusa (cold start)
    Notifications.getLastNotificationResponseAsync().then((response) => {
      const url = response?.notification.request.content.data?.url;
      if (typeof url === 'string') router.push(url);
    });

    // Notifica toccata mentre l'app è aperta o in background
    const sub = Notifications.addNotificationResponseReceivedListener((response) => {
      const url = response.notification.request.content.data?.url;
      if (typeof url === 'string') router.push(url);
    });

    return () => sub.remove();
  }, []);
}

Perché le mie notifiche push non funzionano?

Il 90% dei ticket "le notifiche non arrivano" che ho gestito ricade in cinque categorie, in ordine di frequenza. Prima categoria: ambiente APNs sbagliato. Le build sviluppo con Expo Go o con development client usano il gateway APNs sandbox, mentre le build TestFlight e produzione usano il gateway production. Un token registrato in sandbox non funziona in production e viceversa. Il sintomo classico è "funziona in dev, sparisce quando distribuisco su TestFlight". Soluzione: registra sempre il token dopo la build in produzione e assicurati che il backend distingua l'ambiente.

Seconda categoria: canali Android mancanti o con importance troppo bassa. Se invii una notifica su un canale che non esiste, Android la scarta senza log. Se il canale ha importance: LOW, la notifica arriva ma non fa alcun suono né apparizione heads-up. Terza: permessi rifiutati su Android 13+. Molti sviluppatori dimenticano di gestire POST_NOTIFICATIONS perché in dev su un emulatore precedente funzionava. Quarta: token stale. Se l'utente reinstalla l'app, il token cambia. Chiama getExpoPushTokenAsync a ogni avvio e sincronizza col backend. Quinta: Doze mode e battery optimization su Android; le notifiche con priority normal possono essere ritardate di minuti su dispositivi con batteria bassa. Usa priority: 'high' per notifiche time-sensitive.

Se dopo tutto questo hai ancora problemi di consegna in produzione, controlla anche i log EAS Build (a volte una key APNs revocata dall'Apple Developer Portal passa inosservata per giorni). E se stai gestendo tanti dispositivi, ha senso incrociare il monitoraggio delle notifiche con il resto dell'osservabilità dell'app; la mia guida al debugging in React Native mostra come integrare Reactotron e Hermes Inspector per catturare gli eventi di notifica lato client.

Domande frequenti

Le notifiche push funzionano in Expo Go nel 2026?

Le notifiche push remote non funzionano più in Expo Go su Android dall'SDK 53 in poi, e su iOS hanno sempre avuto limitazioni. Per testare le push in modo affidabile devi usare una build EAS development client o una preview build. Le notifiche locali continuano a funzionare in Expo Go su entrambe le piattaforme.

Devo usare Firebase per le notifiche push in React Native?

Su Android sì, sempre: FCM è l'unico gateway push supportato dal sistema operativo Google e serve anche se usi Expo Notifications. Su iOS non serve Firebase, perché APNs è direttamente accessibile. Puoi comunque scegliere di non toccare Firebase manualmente delegando tutto a Expo Notifications, che gestisce il tuo google-services.json come configurazione.

Qual è la differenza tra notifiche locali e push in React Native?

Le notifiche locali sono schedulate dall'app stessa sul dispositivo e non hanno bisogno di server o connessione internet, quindi sono utili per reminder, timer, allarmi. Le notifiche push (remote) sono inviate da un backend attraverso APNs o FCM e possono arrivare anche quando l'app è chiusa. Entrambe usano expo-notifications ma API leggermente diverse: scheduleNotificationAsync per le locali, getExpoPushTokenAsync più backend per le push.

Quanto costa mandare notifiche push con Expo?

Il servizio Expo Push è gratuito, senza limiti mensili sul volume di notifiche. C'è solo un rate limit tecnico di circa 600 messaggi al secondo per progetto, che si può alzare contattando Expo. FCM e APNs sono anch'essi gratuiti dal 2026, quindi anche un setup nativo puro non ha costi diretti sul trasporto.

Come si testano le notifiche push senza backend?

Expo mette a disposizione il Push Notification Tool, un form web dove incolli un Expo Push Token e invii un messaggio di prova. È il modo più rapido per validare che il device sia registrato correttamente prima di implementare il backend. Per test più realistici con payload complessi conviene usare cURL contro https://exp.host/--/api/v2/push/send.

Jake Morrison
Sull'Autore Jake Morrison

React Native lead engineer who's shipped six apps and learned six different lessons. Bullish on the New Architecture.