Push notifikácie v Expo 2026: Kompletný sprievodca s FCM v1 a APNs

Praktický sprievodca push notifikáciami v Expo pre rok 2026: konfigurácia FCM v1 a APNs, získanie ExponentPushTokenu, odosielanie zo servera, notifikačné kanály na Androide a spracovanie interakcií vrátane cold-startu.

Push Notifikácie v Expo 2026: FCM v1 + APNs

Aktualizované: 9. september 2026

Push notifikácie v Expo 2026 vyžadujú konfiguráciu FCM v1 API pre Android a APNs kľúča (.p8) pre iOS, knižnicu expo-notifications v development builde a získanie ExponentPushToken, ktorý pošlete na Expo Push Service alebo priamo na FCM/APNs. Starý legacy FCM API vypol Google 21. júna 2024, takže každý Expo projekt dnes musí používať Service Account JSON pri EAS credentials. V tomto sprievodcovi si prejdeme celý flow: od app.json konfigurácie cez získanie tokenu na fyzickom zariadení až po serverovú integráciu. Priznám sa, na tomto som si v minulom projekte poriadne polámal zuby (dva dni ladenia kvôli chýbajúcemu POST_NOTIFICATIONS oprávneniu), takže sa vám snažím ušetriť čas.

  • Expo Go od SDK 53 už nepodporuje vzdialené push notifikácie na Androide. Na testovanie potrebujete development build alebo produkčný build.
  • Pre Android v roku 2026 musíte nahrať Firebase Service Account JSON cez eas credentials, legacy FCM Server Key už nefunguje.
  • Pre iOS je jednoduchšie použiť APNs Auth Key (.p8) ako klasický .p12 certifikát. Jeden kľúč funguje pre všetky vaše iOS aplikácie.
  • ExponentPushToken je abstrakcia nad FCM/APNs tokenmi. Ak nechcete používať Expo Push Service, môžete si vytiahnuť natívny token cez getDevicePushTokenAsync().
  • Na Androide 8+ musíte pred prvou notifikáciou vytvoriť notifikačný kanál, inak sa správa nezobrazí.
  • Expo Push Service má limit ~600 notifikácií za sekundu na projekt a batchuje správy po 100 v jednej HTTP požiadavke.

Čo sa v roku 2026 zmenilo

Rok 2026 priniesol niekoľko zásadných zmien v push notifikáciách pre React Native a Expo. Google definitívne vypol legacy FCM API (kľúč vo formáte AAAA...) v júni 2024, takže každý projekt musí migrovať na FCM v1 API so Service Account JSON súborom. Podľa oficiálnej migračnej dokumentácie Firebase nie je možné odosielať notifikácie starým API ani po výnimke.

Zároveň sa v Expo SDK 53 (a odvtedy to platí aj pre SDK 54, ktoré je aktuálne v roku 2026) odstránila podpora vzdialených push notifikácií z Expo Go klienta na Androide. Ak vyvíjate v Expo Router v6 a navigujete používateľa po klepnutí na notifikáciu, potrebujete development build vytvorený cez eas build --profile development. Toto rozhodnutie tímu Expo dáva zmysel. Expo Go zdieľa jednu Firebase inštanciu pre všetky projekty, čo v produkcii nemôže fungovať.

Ďalšou zmenou je uvoľnenie expo-notifications verzie 0.30, ktorá zjednotila API medzi Androidom a iOS, pridala typy pre payloady a odstránila deprecated metódy setNotificationCategoryAsync v starom tvare. Ak migrujete zo starého Expo SDK 51 a nižšie, počítajte s tým, že import cesty expo-notifications/build/Notifications už neexistujú, používajte len verejnú fasádu.

Konfigurácia projektu a app.json

Základom je správne nastavené app.json (alebo app.config.ts ak generujete konfiguráciu dynamicky). Musíte pridať plugin expo-notifications, nastaviť bundleIdentifier pre iOS a package pre Android a definovať entitlements pre aps-environment. Bez týchto krokov EAS Build vygeneruje aplikáciu, ktorá nedostane token. Poznám to z vlastnej skúsenosti, hodinu som hľadal chybu, ktorá bola vlastne v desiatich riadkoch JSONu.

{
  "expo": {
    "name": "Moja App",
    "slug": "moja-app",
    "scheme": "mojaapp",
    "ios": {
      "bundleIdentifier": "sk.mojafirma.mojaapp",
      "supportsTablet": true,
      "infoPlist": {
        "UIBackgroundModes": ["remote-notification"]
      }
    },
    "android": {
      "package": "sk.mojafirma.mojaapp",
      "googleServicesFile": "./google-services.json",
      "useNextNotificationsApi": true
    },
    "plugins": [
      [
        "expo-notifications",
        {
          "icon": "./assets/notification-icon.png",
          "color": "#0ea5e9",
          "defaultChannel": "default",
          "sounds": ["./assets/notification-sound.wav"]
        }
      ]
    ],
    "extra": {
      "eas": {
        "projectId": "XXXX-XXXX-XXXX"
      }
    }
  }
}

Ikona pre notifikáciu na Androide musí byť čisto biela silueta s priehľadným pozadím. Android farby ignoruje. Ak dáte farebný PNG, systém ho zobrazí ako plný biely štvorec a používatelia sa budú sťažovať (a oprávnene, vyzerá to fakt zle). Odporúčaná veľkosť je 96×96 px, ale plugin škáluje aj väčšie verzie. Pre iOS sa používa štandardná ikona aplikácie a farba sa neriadi parametrom color.

FCM v1 API pre Android

V roku 2026 musí každý projekt používajúci Android push notifikácie mať v EAS credentials nahratý Firebase Service Account JSON. Postup je nasledujúci:

  1. V Firebase Console vytvorte projekt a pridajte Android aplikáciu s presne rovnakým package ako v app.json.
  2. Stiahnite google-services.json a umiestnite ho do rootu projektu.
  3. V Firebase Console prejdite na Project Settings → Service Accounts a kliknite na Generate new private key. Stiahne sa vám JSON súbor s privátnym kľúčom.
  4. Nahrajte ho do EAS príkazom:
# Interaktívne cez wizard
eas credentials

# Alebo priamo cez CLI
eas credentials -p android \
  --profile production \
  --non-interactive \
  --fcm-v1-service-account-key ./firebase-service-account.json

Po nahratí EAS Build zabuduje credentials do vášho APK/AAB a Expo Push Service ich použije na volanie FCM v1 endpointu. Ak si chcete overiť, či všetko sedí, spustite eas credentials a v sekcii Android by ste mali vidieť Google Service Account Key For FCM V1 spolu s dátumom nahratia.

APNs kľúč pre iOS

Pre iOS máte dve možnosti: starší APNs Certificate (.p12) alebo modernejší APNs Auth Key (.p8). V roku 2026 dôrazne odporúčame druhú možnosť, pretože jeden kľúč platí pre všetky vaše aplikácie pod rovnakým tímom a nevyprší po roku. EAS ho vygeneruje aj automaticky, ak spustíte build s --auto-submit a nemáte ešte žiadny credentials.

# Nechať EAS spravovať kľúč automaticky (odporúčané)
eas credentials -p ios --profile production

# V menu vyberte:
# 1. Set up Push Notifications for your project
# 2. Set up a new Push Key (Push Notifications Key)

Ak preferujete manuálnu správu, prihláste sa do Apple Developer portálu, prejdite na Keys a vytvorte nový kľúč s povolenou službou Apple Push Notifications service (APNs). Stiahnutý .p8 súbor sa dá stiahnuť len raz, tak si ho hneď zálohujte. Potom v EAS zvoľte Upload a Push Key a zadajte Team ID (10-znakový identifikátor tímu), Key ID (10 znakov, viditeľné pri kľúči v portáli) a cestu k .p8 súboru.

Podľa oficiálnej Apple UserNotifications dokumentácie je APNs prostredie riadené aps-environment entitlementom. V development builde má hodnotu development, v release production. Expo Push Service to rieši za vás automaticky. Ak posielate priamo cez APNs, musíte použiť správny host (api.sandbox.push.apple.com vs api.push.apple.com).

Ako získať push token v aplikácii

Token získate volaním getExpoPushTokenAsync po tom, ako používateľ udelí povolenie. Nasledujúci hook zapuzdruje celý flow a je vhodný na použitie v koreni aplikácie. Osobne ho volám v komponente najbližšom k Providerom, aby si aplikácia token uložila hneď po prihlásení.

import { useEffect, useState } from "react";
import { Platform } from "react-native";
import Constants from "expo-constants";
import * as Notifications from "expo-notifications";
import * as Device from "expo-device";

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

export function usePushToken() {
  const [token, setToken] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    (async () => {
      if (!Device.isDevice) {
        setError("Push notifikácie fungujú len na fyzickom zariadení.");
        return;
      }

      if (Platform.OS === "android") {
        await Notifications.setNotificationChannelAsync("default", {
          name: "Predvolený kanál",
          importance: Notifications.AndroidImportance.DEFAULT,
          vibrationPattern: [0, 250, 250, 250],
          lightColor: "#0ea5e9",
        });
      }

      const settings = await Notifications.getPermissionsAsync();
      let status = settings.status;
      if (status !== "granted") {
        const req = await Notifications.requestPermissionsAsync();
        status = req.status;
      }
      if (status !== "granted") {
        setError("Používateľ nepovolil notifikácie.");
        return;
      }

      const projectId = Constants.expoConfig?.extra?.eas?.projectId;
      if (!projectId) {
        setError("Chýba EAS projectId v app.json.");
        return;
      }

      const result = await Notifications.getExpoPushTokenAsync({ projectId });
      setToken(result.data);
    })();
  }, []);

  return { token, error };
}

Token má tvar ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx] a je stabilný. Nemení sa pri reštarte aplikácie, ale môže sa zmeniť po preinštalovaní alebo po tom, čo systém zresetuje FCM registráciu. Odporúčame ukladať ho na server a pri každom spustení aplikácie ho revalidovať (napr. cez PATCH endpoint /users/me/push-token). Pri odhlásení používateľa token na serveri zmažte.

Odosielanie notifikácií zo servera

Najjednoduchšia cesta je použiť Expo Push Service, teda HTTP endpoint, ktorý za vás rieši batching, retry logiku a distribúciu na FCM/APNs. Podľa oficiálnej dokumentácie Expo stačí POST na https://exp.host/--/api/v2/push/send s pole ExpoPushMessage objektov.

// Node.js server, TypeScript
import fetch from "node-fetch";

type ExpoPushMessage = {
  to: string | string[];
  title?: string;
  body?: string;
  data?: Record<string, unknown>;
  sound?: "default" | null;
  badge?: number;
  channelId?: string;
  priority?: "default" | "normal" | "high";
  ttl?: number;
};

export async function sendPush(messages: ExpoPushMessage[]) {
  // Expo prijíma max 100 správ v jednej požiadavke
  const chunks: ExpoPushMessage[][] = [];
  for (let i = 0; i < messages.length; i += 100) {
    chunks.push(messages.slice(i, i + 100));
  }

  const tickets = [];
  for (const chunk of chunks) {
    const res = 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(chunk),
    });
    const json = await res.json() as { data: unknown[] };
    tickets.push(...json.data);
  }
  return tickets;
}

// Príklad použitia
await sendPush([{
  to: "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]",
  title: "Nová objednávka",
  body: "Objednávka #4821 čaká na potvrdenie.",
  data: { orderId: "4821", type: "order.pending" },
  sound: "default",
  channelId: "orders",
  priority: "high",
}]);

Odpoveď obsahuje pole tickets. Každý ticket má status: "ok" alebo "error". Ak dostanete DeviceNotRegistered, znamená to, že token je invalidný (používateľ aplikáciu odinštaloval alebo zakázal notifikácie) a musíte ho zo svojej databázy odstrániť. Kompletné potvrdenie doručenia však ticket negarantuje. Treba po pár minútach polnúť receipty cez endpoint /push/getReceipts.

Ak potrebujete garancie doručenia s konkrétnymi milisekundovými latenciami (napríklad pri chat aplikáciách), zvážte priamu integráciu s FCM v1 a APNs. Vyžaduje to viac kódu (podpisovanie JWT pre APNs, OAuth token pre FCM), ale získate plnú kontrolu. Dobrým doplnkom je použiť aj EAS Update na OTA distribúciu opráv, ak niekedy potrebujete rýchlo opraviť handler bez čakania na store review.

Notifikačné kanály na Androide

Od Androidu 8 (API 26) musí každá notifikácia patriť do notifikačného kanála. Kanály sa vytvárajú raz pri prvom spustení aplikácie a používateľ ich vidí v systémovom nastavení, kde môže samostatne stlmiť napríklad marketingové správy, ale nechať aktívne transakčné. Dobrá segmentácia kanálov výrazne zvyšuje retention. (Testovali sme to v jednom e‑commerce projekte a rozdelenie „objednávky vs novinky“ znížilo počet odhlásení o polovicu.)

import * as Notifications from "expo-notifications";
import { Platform } from "react-native";

export async function setupChannels() {
  if (Platform.OS !== "android") return;

  await Notifications.setNotificationChannelAsync("orders", {
    name: "Objednávky",
    description: "Notifikácie o stave objednávok",
    importance: Notifications.AndroidImportance.HIGH,
    sound: "default",
    vibrationPattern: [0, 300, 200, 300],
    lightColor: "#22c55e",
  });

  await Notifications.setNotificationChannelAsync("marketing", {
    name: "Novinky a akcie",
    description: "Občasné informácie o zľavách",
    importance: Notifications.AndroidImportance.LOW,
    showBadge: false,
  });

  await Notifications.setNotificationChannelAsync("chat", {
    name: "Chat správy",
    description: "Priame správy od používateľov",
    importance: Notifications.AndroidImportance.MAX,
    sound: "chat.wav",
    enableVibrate: true,
  });
}

Pri odosielaní správy z Expo Push Service uveďte channelId, ktorý sa má použiť. Ak channelId neexistuje na zariadení, Android použije predvolený kanál a správa sa ticho zahodí vo verzii Android 13+, ak používateľ nikdy neschválil POST_NOTIFICATIONS oprávnenie. Práve toto oprávnenie je od Androidu 13 samostatné a musíte oň žiadať cez requestPermissionsAsync(). Bez neho zariadenie dokonca ani nezaregistruje FCM token.

Spracovanie interakcií a deep linkov

Notifikácia je len začiatok. Používateľa treba dopraviť na správnu obrazovku. Modul expo-notifications poskytuje dva listenery: addNotificationReceivedListener (spustí sa, keď príde správa a aplikácia je aktívna) a addNotificationResponseReceivedListener (spustí sa, keď používateľ na notifikáciu klepne).

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

export function useNotificationRouting() {
  const router = useRouter();

  useEffect(() => {
    // Cold start — aplikácia sa spustila cez notifikáciu
    (async () => {
      const last = await Notifications.getLastNotificationResponseAsync();
      if (last) handleResponse(last);
    })();

    const sub = Notifications.addNotificationResponseReceivedListener(handleResponse);
    return () => sub.remove();

    function handleResponse(response: Notifications.NotificationResponse) {
      const data = response.notification.request.content.data as {
        type?: string;
        orderId?: string;
        chatId?: string;
      };
      if (data.type === "order.pending" && data.orderId) {
        router.push(`/orders/${data.orderId}`);
      } else if (data.type === "chat.message" && data.chatId) {
        router.push(`/chat/${data.chatId}`);
      }
    }
  }, [router]);
}

Kľúčové je vyriešiť cold start scenár: používateľ klepne na notifikáciu, aplikácia sa spustí od nuly a listener sa zaregistruje neskôr, ako správa dorazí. Preto na začiatku volajte getLastNotificationResponseAsync(), ktoré vráti poslednú interakciu, ak nejaká bola. Podľa referenčnej dokumentácie expo-notifications je toto jediný spôsob, ako zachytiť cold-start.

Ak máte pokročilé požiadavky (napríklad tlačidlá Prijať / Odmietnuť priamo v notifikácii), použite setNotificationCategoryAsync na definovanie akcií. Response listener potom obsahuje actionIdentifier, podľa ktorého sa rozhodujete. Táto integrácia sa dobre kombinuje s TanStack Query mutáciami, kde akcia môže spustiť mutation bez otvorenia aplikácie na iOS 16+.

Testovanie a debugovanie

Najrýchlejšia cesta na test je Expo Push Tool, teda webový formulár na expo.dev/notifications, kde vložíte ExponentPushToken, napíšete titulok a odošlete. Odpoveď sa zobrazí okamžite a vidíte payload, ktorý dostane zariadenie. Ideálne na overenie, či token funguje a či zariadenie správu dostáva.

# Rýchly test cez curl
curl -H "Content-Type: application/json" \
     -X POST https://exp.host/--/api/v2/push/send \
     -d '{
       "to": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]",
       "title": "Test",
       "body": "Toto je testovacia notifikácia",
       "sound": "default"
     }'

Ak správa nedôjde, prejdite si tento checklist v poradí:

  1. Testujete na fyzickom zariadení? Simulátor iOS podporuje notifikácie od iOS 16, ale Android emulátor len obmedzene a bez FCM tokenu.
  2. Aplikácia je development build alebo produkčný build? Expo Go v Androide 2026 push neposiela.
  3. Máte v eas credentials nahratý FCM v1 Service Account a APNs Push Key?
  4. Používateľ udelil povolenie? Overte cez getPermissionsAsync().
  5. Na Androide 13+ udelil aj POST_NOTIFICATIONS?
  6. Kanál channelId existuje na zariadení?
  7. V receipte od Expo Push Service je status: "ok"? Ak nie, aká je chybová hláška?

Pre systematický monitoring odporúčame logovanie push receiptov do Sentry alebo vlastnej DB. Analytika typu delivered → opened → converted vám ukáže, či rozdelenie kanálov skutočne zlepšilo engagement. Ak vyvíjate s pomocou React Native DevTools, môžete si notifikačné listenery breakpointovať priamo v Chromiu a preinšpektovať payload v reálnom čase.

Často kladené otázky

Ako otestujem push notifikácie v Expo bez servera?

Použite Expo Push Tool na expo.dev/notifications. Stačí prihlásiť sa cez svoj Expo účet, vložiť ExponentPushToken zo zariadenia a odoslať správu z prehliadača. Alternatívou je jednoduchý curl príkaz proti endpointu https://exp.host/--/api/v2/push/send.

Potrebujem Firebase pre push notifikácie v Expo iOS aplikácii?

Nie. Pre iOS stačí APNs Auth Key (.p8) alebo certifikát. Firebase je nutný len pre Android, presnejšie Service Account JSON pre FCM v1 API. Ak posielate notifikácie cez Expo Push Service, servis to všetko rieši za vás.

Prečo mi push notifikácie nefungujú v Expo Go?

Od Expo SDK 53 sú vzdialené push notifikácie na Androide v Expo Go zámerne vypnuté. Musíte si vytvoriť development build príkazom eas build --profile development --platform android a testovať v ňom. iOS Expo Go stále push notifikácie podporuje.

Ako pošlem push notifikáciu z Node.js servera?

Najjednoduchšie cez POST na https://exp.host/--/api/v2/push/send s JSON payloadom obsahujúcim pole správ (max 100 na požiadavku). Alternatívne použite oficiálny balík expo-server-sdk, ktorý rieši chunking, retry a získavanie receiptov automaticky.

Aký je limit push notifikácií cez Expo Push Service?

Expo Push Service má mäkký limit približne 600 správ za sekundu na projekt a batchuje po 100 správach na jednu HTTP požiadavku. Pri veľkých kampaniach rozložte odosielanie v čase alebo prejdite priamo na FCM v1 a APNs, ktoré majú vyššie sľubované throughputy.

O Autorovi Editorial Team

Our team of expert writers and editors.