Expo Push

expo-notificationsNotifications35 lines

Push and local notifications through Expo's push service.

Current — as published in v1.3.2. Written by hand for Expo Push, not generated.

The rule

Permission

  • Ask in context, after explaining the benefit — never on first launch. On iOS the system prompt appears once per install; a decline is effectively permanent, and you cannot ask again.
  • Show your own explanation screen first and only call requestPermissionsAsync() when the user agrees.
  • Denied is a normal state, not an error. The app must work fully without push.

Tokens

  • Refresh on every launch — tokens rotate on reinstall and device restore.
  • Send to the backend with the user id; delete on sign-out, or the next user on that device receives the previous user's notifications.
  • Purge tokens that come back DeviceNotRegistered in a receipt.

Handling

  • Register the response listener once, at startup.
  • Handle the cold start with getLastNotificationResponseAsync(). A tap usually launches the app from scratch, and without this the deep link is lost.
  • Android needs an explicit notification channel or importance is ignored.

Sending

  • A 200 from the push API means accepted, not delivered. Check receipts.
  • Never put personal data or a token in a notification payload — it is visible on the lock screen and passes through Apple's and Google's infrastructure.

Never

  • Never expect push to work in a simulator or in Expo Go.
  • Never block a feature on notification permission.

As each tool receives it

The same guidance, in the shape each editor reads. Cursor and Claude Code are written separately by hand; the rest are derived from the Cursor rule.

.cursor/rules/expo-notifications.mdchand-written for this tool
.cursor/rules/expo-notifications.mdc
---
description: Expo push notification conventions
globs: ["src/services/notifications/**", "src/features/**"]
alwaysApply: false
---

# Expo Push

## Permission

- Ask **in context**, after explaining the benefit — never on first launch.
  On iOS the system prompt appears once per install; a decline is effectively
  permanent, and you cannot ask again.
- Show your own explanation screen first and only call
  `requestPermissionsAsync()` when the user agrees.
- Denied is a normal state, not an error. The app must work fully without push.

## Tokens

- Refresh on every launch — tokens rotate on reinstall and device restore.
- Send to the backend with the user id; **delete on sign-out**, or the next user
  on that device receives the previous user's notifications.
- Purge tokens that come back `DeviceNotRegistered` in a receipt.

## Handling

- Register the response listener once, at startup.
- Handle the **cold start** with `getLastNotificationResponseAsync()`. A tap
  usually launches the app from scratch, and without this the deep link is lost.
- Android needs an explicit notification channel or importance is ignored.

## Sending

- A 200 from the push API means accepted, not delivered. Check receipts.
- Never put personal data or a token in a notification payload — it is visible
  on the lock screen and passes through Apple's and Google's infrastructure.

## Never

- Never expect push to work in a simulator or in Expo Go.
- Never block a feature on notification permission.

What else this module writes

Selecting Expo Push contributes more than a rule file — all of it merged with every other module you pick, with conflicts resolved rather than duplicated.

Environment
EXPO_PUBLIC_EAS_PROJECT_IDrequiredEAS project id, needed when requesting a push token. Also present in `app.json`.
EXPO_ACCESS_TOKENoptionalAuthenticates sends against the Expo push API. Server-side only.
Dependencies
expo-notifications^57.0.0expo-device^57.0.0
Folders
src/services/notifications/

Related

Put this in your repo

Adds Expo Push to a project this tool generated, without starting over. Files you have hand-edited are left alone.