メインコンテンツにジャンプ
Mobile Guides

Master Expo Push Notification Guide 2026

Master Expoのプッシュ通知設定ガイドです。このガイドでは、パーミッション、トークン、送信、ハンドリング、生産性の向上のためのベストプラクティスをカバーしています。

マーティン・ドナディュー

マーティン・ドナディュー

コンテンツマーケター

Master Expo Push Notification Guide 2026

アプリが動作し、ユーザーがサインインし、製品が再エンゲージフローのようなネイティブなフィールを実現したいと思っている場合、カートのリマインダー、レビューの誘導、新しいメッセージのアラート、リリースのアナウンスなどが必要になります。最初のインスピレーションは、「プッシュを簡単に組み込む」ということです。ただし、1週間後には、1台のデバイスがアラートを受け取っているのに、シミュレータが正常に登録されているのに、誰も説明できないように、タップが正しい画面を開くことができないことがわかります。

Expoのプッシュ通知は、実用的なレイヤーをAPNsとFCMに提供するため、React Nativeチームにとって実用的です。しかし、デモと生産性のある実装の間のギャップは実際です。トークンライフサイクル、パーミッションのタイミング、リスナーのセットアップ、ペイロードの設計、バックエンドのクリーンアップなど、すべて重要です。さらに、頻繁にアプリロジックの変更を実施している場合、信頼性の高いメッセージングとリリース速度のために、運用上の規律が必要になります。そうしたより広い懸念は、

Martin Donadieu モバイル アプリのユーザー保持作業: そのユーザー エクスペリエンスの周りが予測可能である場合にのみ、配信は有用です。

目次

Expo Push Notificationとユーザーとの関わりを促進するための基礎

An Expo Push Notification セットアップは、1つの理由で魅力的です。複雑なネイティブメッセージングをチームが最初の日から所有したくないため、多くのネイティブメッセージングの複雑さを削減します。APNsとFCMの直接プラumbingを最初に構築するのではなく、Expoのゲートウェイと協力して、製品の動作、ルーティング、許可のUX、バックエンドのメッセージロジックに焦点を当てます。

アブストレクションは、プッシュが「実際に」なり得ないことを意味するのではなく、エンジニアリングの努力の場所を変えるだけです。

サービスは、パフォーマンスが通常は心配することの最初のことではありません。2023年3月14日から2023年6月12日まで、Expoのプッシュ通知APIは、 42ミリ秒のメディアン応答時間, 273ミリ秒のp99遅延、日々のエラーレートが0.17%の 数百万のメッセージを送信する KnockのExpoプッシュ__CAPGO_KEEP_0__ベンチマーク分析 実際には、Expoは「Expoプッシュ」という言葉は、チームが言っているのは Knock’s Expo push API benchmark analysisExpoはメッセージを

APNsに転送します

APNs

  • APNs APNs APNs iOSとAndroid向け FCM Android向け。
  • Token形式: サーバーはExpo Push Tokenを管理する代わりに、プラットフォーム固有のトークン処理を初期化します。
  • リクエスト契約: ExpoのプッシュAPIにペイロードをPOSTします。native provider APIを直接統合するのではなく。

That’s helpful, but it also creates a common misunderstanding. Teams sometimes assume Expo owns every delivery problem. In practice, many failures come from app code, stale tokens, malformed payloads, or poor permission flow.

実践的なルール: Expoを信頼できるトランスポート層として扱い、クライアントとバックエンドの設計に問題がないことを確認すること。

実際の意味は

実稼動用に準備されていることを意味します

問題 デモの考え方 実稼働の考え方
権限 すぐに質問する ユーザーの価値が明確になるまでに質問する
トークン 一度だけ保存する 更新、重複、期限切れ、整合性のとれ
ペイロード すべてを含める data ペイロードを小さく、行動指向的にする
アプリの動作 アラートを表示 正しくナビゲートし、前景状態を処理
オペレーション 手動テスト 受領書、クリーンアップ、ログ、インシデントハンドリング

「通知を送信する」と「通知が実際の製品フローをサポートする」というのはどちらが違うのか

初期プロジェクト設定と構成

Expo プッシュの多くの痛みは、最初の許可ダイアログの前に始まる。プロジェクトの構成が雑だとすると、クライアントcodeは正しく見えながら、ビルドごとにアプリが一貫して動作しない

モダンなラップトップが木の机の上に置かれ、プロジェクト設定の構成codeファイルが表示されている

正しいライブラリがインストールされているかつ、開発環境がビルドパスと一致していることを確認してください。Expo Go よりも幅広い作業を行っている場合は、カスタムの Expo 開発クライアント設定と開発環境を合わせることが役に立つ 開発環境を設定する、通知の動作を検証するために、生産環境に近いビルドが必要になることがよくあります。

通知パッケージをインストールする

最低限、通常は以下のものが必要になります。

  • expo-notifications 許可の要求、トークンの取得、リスナー、通知の表示のために
  • expo-device トークンの取得を守るために Device.isDevice.

通常のインストールコマンドはパッケージマネージャーに依存しますが、主なことはエクスポのバージョンと一致させることです。SDKとエクスポのバージョンを混ぜることは避けるべきです。エクスポが互換性のあるバージョンを解決するようにします。

プロジェクトのレベルで設定を追加する

最小限の設定 app.json または app.config.js 設定は明示的でなければなりません。通知はアプリの契約の一部であり、後思いついたものではありません。

{
  "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"
      }
    }
  }
}

いくつかの詳細はここで重要です。

  • バンドル識別子とパッケージ名 実際に配信するアプリと一致させる必要があります。
  • The notifications plugin ネイティブプロジェクトがビルド時に必要な設定を取得することを保証します。
  • The EAS project ID トークン取得が正しいエクスポプロジェクトと関連付けられていることを期待している場合、重要です。

通知ハンドラーを早く設定する

基本的なチュートリアルでは、通知の動作を定義するのに遅すぎています。アプリ起動時に近く設定してください。そうすると、前景動作が予測可能になります。

import * as Notifications from 'expo-notifications';

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

チームは、前景通知がアラートを表示するか、サウンドを再生するか、バッジを変更するかを決定します。製品によっては、同じ決定が取れません。チャットアプリと決済アプリは同じ選択肢を選択しません。

前景動作を意図的に定義しないと、チームは実際に受信したが、製品が期待しているように表示されなかった通知をデバッグすることになります。

Androidではチャンネル設定が必要です。

Androidの通知チャンネルは実用上はオプションではありません。チャンネルを省略すると、アラートが不一致またはユーザーの期待に合わない可能性があります。

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,
  });
}

アプリの初期化時に設定してください。次に、チャンネルIDを安定させてください。チャンネルIDを気軽に変更すると、通知の動作を推論するのが難しくなります。

許可を求め、プッシュ トークンをキャプチャする

多くのチームはスニペットからこの部分をコピーし、最終的には後悔する

許可の要求にはタイミング、プラットフォームの認識、非同期の厳格なハンドリングが必要です。トークン キャプチャには、物理デバイス上で実行される必要があり、許可が解決されるまで待ち、結果をすぐにバックエンドに保存する準備ができている場合にのみ実行されます。

Expo プッシュ通知トークンの取得と保存のプロセスを示すステップバイステップの図

クライアント関数が基本的な基準

この関数を使用して、基準点として使用してください

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 };
}

順序は重要です。デバイスのタイプを確認し、Android チャネル動作を設定し、許可を解決し、プロジェクトの構成を検証し、Expo トークンを要求します。

なぜ Device.isDevice 省略することはできない

このような間違いは、見た目は無害に見えるが、多くのノイズを生み出す。専門チームは条件付きで許可を要求し、 Device.isDevice は真実であり、開発者が無効のシミュレータ トークンに通知を送信し、Expo に問題があると非難するのは、実際にはアプリの構成に関する説明されているように、実際の問題であるは省略するのは大きな間違いです Eagerworks’ Expo 通知実装ノート.

なぜチェックは関数の先頭にあるのか。ヘルパーを隠すのではなく、明らかにする。

シミュレータの結果はUIテストに役立ちますが、プッシュトークンの登録を検証するのに信頼できません。

許可を求める時期を選ぶ

スプラッシュ画面では許可を求めない。ユーザーが価値を理解する前に許可を求めない。通常、ユーザーがアクションを実行した後、通知の利点が具体化される時点で許可を求めるのが最も適切です。例えば、配信更新を有効化したり、会話に参加したり、視聴中のアイテムを保存したりすると許可を求めることができます。

良い実装は通常このフローを遂行します。

  1. ユーザーが意味のある機能境界に到達します。
  2. アプリは自分のUIで通知の価値を説明します。
  3. アプリはシステムの許可を求めます。
  4. アプリは許可が付与された場合にすぐにバックエンドでトークンを保存します。

最後のステップで多くのアプリが失敗します。トークンを取得し、ローカルにログし、バックエンドの登録を延期します。後でサポートは、どのデバイスがどの時点でどのトークンを持っていたかを判断できません。

トークンの登録後すぐにトークンを保存する簡単な例

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;
}

後続のワークフローでは、このガイドは、視覚的な参照として役立ちます:

リリース重視のアプリを開発するチームにとって、トークン登録をアプリの運用状態の一部として考えることも役立ちます。オンボーディングの一部としてだけではなく、より広範な Expoアプリ配信ワークフローで、アプリの動作が頻繁に変わり、バックエンドの状態を同期する必要がある場合に、適切なアプローチです。

サーバーから通知を送信する

バックエンドが有効なExpo Push Tokenを持っている場合、通知を送信することは簡単です。リクエスト自体が難しい部分ではありません。payloadの内容とクライアントの状態に置ける信頼の度合いを決めるのが難しい部分です。

Nodeスタイルの最小限の例 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;
}

payloadの各フィールドが何を実現するか

payloadを捨て場として扱わないでください。各フィールドが意図的に使用されていることを確認してください。

フィールド 目的 実践的なアドバイス
to ターゲットエクスポッシュートトークン __CAPGO_KEEP_0__を現在のデバイスレコードに属していることを検証する
title 通知ヘッダー 短く人間が読みやすいようにしてください
body 主な表示テキスト アクションを明確にする
sound システムサウンドの動作 高価なアラートの場合のみ、sparingly使用してください
data アプリ固有のメタデータ IDやルートヒントを優先して、リッチなコンテンツを避けてください

その data オブジェクトは、製品ワークフローで役に立つ場所です。型とレコードIDを渡すことができ、ユーザーがタップすると最新のデータを取得できます。そのほうが安全です。大量のデータや敏感なデータを直接ペイロードに埋め込むのではなく

ペイロードを小さく、面白くないものに保つ

__CAPGO_KEEP_0__の エクスポの通知のためのコウティエールのガイドエクスポのプッシュトークンは、短期間しか使わないものとみなすべきです。ペイロードのサイズが約 4 KB を超えると、失われる可能性があります。信頼できるパターンは、小さなメタデータペイロードを送信することです。例えば { "type": "new_review", "id": 123 } ではなく、大きなJSONやインラインメディアを送信するのではなく。

実際のシステムでは、機能するアドバイスです。小さなペイロードは、より少しが失敗し、アプリロジックが変更されたときに長く生き残ります。

ユーザーをルーティングするのに十分なデータを送信し、開いた後は残りのデータを取得する。

有用なサーバーサイドの習慣

  • テスト用の基本的な送信関数は十分ですが、実際のものは、以下の責任を追加することが多いです。 送信試行を永続化する:ユーザーID、トークン、ペイロードタイプ、タイムスタンプを保存する。
  • コンテンツ生成とトランスポートを分離する: メッセージのコピーを1つのレイヤーで作成し、Expo API リクエストを別のレイヤーで実行する。
  • 無効化のフィードバックを処理する: Expoが後で報告した場合、 DeviceNotRegistered、そのトークンを古いものとみなして無視し、無理にリトライしない。
  • Webhookに適した設計を使用する: あなたのシステムがイベントを発行している場合、同じ種類の バックエンドWebhook処理パターンを使用して、通知トリガーをルーティングする。 デバッグ用のクライアントリスナーを開始する前に、手動テストプッシュを送信する。トークンが小さなペイロードを持つ単純な通知を受け取った場合、トランスポートパスは正常である可能性があります。そうでない場合は、ナビゲーション__CAPGO_KEEP_0__を変更するのではなく、トークン、ペイロードの形状、許可状態を検証することから始める。

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.

配信は機能の半分だけです。通知が到着したときとユーザーがタップしたときに、アプリが何か意味のあることを行う必要があります。

__CAPGO_KEEP_0__

That means handling two separate moments:

  • アプリが開いているときに通知が届く
  • システムトレイやロック画面から通知にユーザーが反応する

A flowchart diagram illustrating the push notification lifecycle for mobile applications in foreground and background states.

フォアグラウンド受信とユーザー反応は異なるイベント

A reliable setup usually includes both listeners:

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 アプリがアクティブなときに実行される addNotificationResponseReceivedListener ユーザーが通知をタップしたときに実行される。組み合わせないでください。ユーザー体験の異なるパスを提供します。

Read the data payload and navigate intentionally

タップハンドリングの実践的なパターン

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');
}

This pattern stays resilient because the payload contains routing hints, not whole documents. If the order has changed since the notification was sent, the app can fetch current server state after navigation.

タップされた通知は、ユーザーが直感的に目的地に到達できるようにするべきです。フォールバックパスが曖昧であれば、ユーザーはすぐに気づきます。

ユーザー コンテキストに合わせてフロントエンドの動作を合わせる必要があります。

アプリがすでに開いている場合、システムスタイルのアラートを無視して表示すると、不自然に感じることがあります。時々、適切なアクションは、インアプリバナー、バッジの更新、または静的なリフレッシュです。サポートのインボックス画面では、ユーザーがすでにその会話を読んでいる場合、可視的なアラートが必要ない場合があります。

なぜなら、フロントエンドリスナーはルートと通知タイプによってbranchするべきだからです。たとえば:

  • チャット画面が開いている場合: メッセージを追加し、冗長なバナーを避けます。
  • ダッシュボードが開いている場合: 軽量なインアプリトーストを表示します。
  • 重要なアカウントイベントの場合: 強力なUI処理を表面化します。

シンプルなアプローチは次のようになります。

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
}

アプリがこれらのコンテキストを区別しない場合、ユーザーは通知疲れを早く感じることになります。技術的に正しい通知の配信でも、ユーザーは疲れを感じることになります。

プロダクション ベスト プラクティスと一般的な落とし穴

最も壊れたExpoプッシュ通知の設定の多くは、Expoが機能が限られているため失敗しない。失敗するのは、チームがトークンが永久的なものであると仮定し、ペイロードに何でも入ることができ、通知ロジックがアプリの更新に影響を受けないと仮定しているからです。

実際の運用環境ではその仮定は生き残りません。

モバイルアプリのプロダクションプッシュ通知を管理するための8つのベストプラクティスを示すチェックリストのイラストグラフィック。

トークンは、アイデンティティレコードではなく、時期の経過とともに変化するものです。

Expo Push Tokenは、ライフタイムデバイスIDとしてではなく、leaseとして扱うべきです。トークンは再インストール、OSの変更、ライフサイクルイベントなどによって回転する可能性があります。トークンが最終的に戻ってきた場合、バックエンドではそれを有効として扱うべきではありません。 DeviceNotRegistered実用的なバックエンドモデルでは、以下の情報を保存する必要があります。

ユーザーID

  • プラットフォーム
  • インストールスコープのメタデータ
  • 現在のトークン
  • 最後に確認されたタイムスタンプ
  • __CAPGO_KEEP_0__
  • __CAPGO_KEEP_0__

ユーザー情報にトークンを保存しないでください。ユーザーは複数のデバイスを使用し、デバイスの状態は変化します。

リフレッシュ戦略は、多くのチュートリアルが認めていないほど重要です。

公式のエコシステムガイドラインでは、実際の運用上のギャップが残っています。既存のExpoプッシュ通知のコンテンツは、App StoreのレビューサイクルとOTAの更新でトークンの有効性を維持する方法を説明していません。 App StoreのレビューサイクルとOTAの更新で、プッシュ通知の信頼性はトークンの現在の状態とバックエンドの同期に依存します。これは、Expoの通知ドキュメントで指摘されています。これは、プッシュ通知の信頼性に影響を与えるため、チームがライブ変更を配信する場合に特に重要です。 これは、デザインされたリフレッシュトリガーに影響を与えるため、重要です。トークンの状態を一致させる良いタイミングは次のとおりです。.

アプリの更新後にアプリを起動する

  • ユーザーがサインインする
  • パーミッション設定が変更される
  • クレデンシャルローテーションを実行する
  • リリースプロセスに組み込む
  • プッシュ関連のサポートチケットの後、復旧フロー

スプリントの最後にセキュリティとコンプライアンスは存在すべきではない

エキスポのチュートリアルは、多くがメカニズムに焦点を当て、運用上のリスクを省略している。趣味用のアプリでは問題ないが、医療関連、金融技術、規制された商業製品では問題がある

エキスポの通知のギャップについてのエンタープライズ向けの議論 エキスポの通知のギャップについてのエンタープライズ向けの議論

  • エキスポの通知のギャップについてのエンタープライズ向けの議論
  • エキスポの通知のギャップについてのエンタープライズ向けの議論
  • エキスポの通知のギャップについてのエンタープライズ向けの議論
  • エキスポの通知のギャップについてのエンタープライズ向けの議論

エキスポの通知のギャップについてのエンタープライズ向けの議論 app store compliance and API security practicesエキスポの通知のギャップについてのエンタープライズ向けの議論は、consent ロギング、監査トレイル、敏感なペイロードの露出を最小限に抑える実践的なガイダンスの欠如を強調している。直接的なエンジニアリングの取り組みは単純である。

プッシュ通知はユーザー向けのメッセージですが、分散システムの問題でもあります。認証状態や支払イベントと同じ注意を払ってください。

通常は機能し、通常は機能しません

通常は機能します 明確な値の説明後に許可を求める
最初のフレームでポップアップ 実機でテスト
シミュレータの登録を信頼 デバイスのコンテキストと共にトークンを保存
ユーザー レコードごとに 1 つのトークン 小さなメタデータ ペイロードを送信
大きなまたは敏感なブロブを埋め込む Push notifications are user-facing messages, but they’re also a distributed systems problem. Treat them with the same care you apply to __CAPGO_KEEP_0__ state and __CAPGO_KEEP_1__ events.
前景とタップイベントを別々に処理する すべての通知が一つのパスを遷移することを前提としている
古いトークンを積極的に期限切れにする 死んだトークンを永久にリトライする

__CAPGO_KEEP_0__


Expo のプッシュ設定は堅実である。複雑ではない。 Capgo はおすすめです。

Capacitor アプリ向けのリアルタイム更新

ウェブ層のバグが生じた場合、Capgo を通じて修正を配信し、アプリストアの承認待ちになるのを避けましょう。ユーザーはバックグラウンドで更新を受け取り、ネイティブの変更は通常のレビュー経路を通じます。

スタートする

ブログの最新記事

Capgoは、プロフェッショナルなモバイルアプリを作成するために必要な最良の洞察を提供します。