Expo Push

Push and local notifications through Expo's push service.

adds it to an existing project · no account needed
Currentas published in v1.5.1

The rule

This is the whole text, exactly as your agent receives it. Nothing is held back for the paid tier.

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.

6 formats, one per tool

Each tab is the file that tool actually reads, at the path it actually looks in. Knowing where each one looks is most of the work of supporting it.

.cursor/rules/expo-notifications.mdchand-written
---
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.

Hand-written by the module author, frontmatter and all. It is the source the four derived formats are rendered from, so a correction lands here first.

What else this module writes

The rule is one file of several. Selecting Expo Push contributes all of this too — 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/

Advisory history

Every time this rule turned out to be wrong, and what we did about it.

No corrections yet

This rule has been accurate since it was published. That is a fact about the rule, not a promise about the future — which is the whole reason this section exists.

Pro tells you the day a correction lands that affects a repo you actually have.

See what Pro adds →

Rules people add alongside this one

Put this rule in a real project

The wizard picks the rest of the stack with you, writes all 6 formats, and leaves a manifest so check can tell you when any of it drifts.