Documentation / Expo SDK

SDKs · React Native

Expo SDK

One package for native Android and iOS push in your Expo app. Includes device registration, rich notifications and receipt/click reporting.

On this page

Before you start

  • @notirabbit/expo 0.5.0 supports Expo SDK 53, 54, 55, 56 and 57.
  • Use a development or store build. Expo Go cannot load this native module.
  • Configure Android and/or iOS in the dashboard. The package and bundle identifiers must match your app.
  • For Android, configure FCM credentials; for iOS, configure APNs credentials. Copy the public app key from App Settings → Keys & IDs.

Use the dependency versions selected by expo install for your Expo SDK. On SDK 53/54, expo-notifications and expo-secure-store use their own version numbers rather than the Expo SDK number. CI runs expo prebuild with the packed package on all five SDK lines. Native compilation is recorded on SDK 53, 55 and 57, and device checks on SDK 55 and 57, rather than every supported combination. See the SDK development notes (repository access required) for the tested build matrix and toolchain requirements.

1. Install the package

The package is hosted on GitHub Packages. Your account needs package access and a GitHub token with read:packages. Configure the registry with a token supplied through NPM_TOKEN:

.npmrc
@notirabbit:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${NPM_TOKEN}
Install dependencies
npx expo install @notirabbit/expo expo-notifications expo-secure-store

Keep the token in your local environment or EAS build secrets. It is a package download credential, not part of the mobile SDK configuration.

2. Configure and build

Add the plugin to your Expo configuration. Use your Firebase project’s Android client configuration for google-services.json.

app.json
{
  "expo": {
    "ios": { "bundleIdentifier": "com.example.app" },
    "android": {
      "package": "com.example.app",
      "googleServicesFile": "./google-services.json"
    },
    "plugins": [
      ["@notirabbit/expo", { "appKey": "YOUR_PUBLIC_APP_KEY" }]
    ]
  }
}

Only the public app key belongs in this configuration. Upload the FCM service-account and APNs key to the dashboard.

No endpoint environment variables needed. The SDK includes the Notirabbit API and event URLs. The plugin creates the iOS notification extension and App Group, and registers the Android messaging service.

Create a native development build
npx expo prebuild\neas build --platform all --profile development

Configure an EAS development profile first. iOS signing must include the main app, Push Notifications, the generated extension and their shared App Group. Rebuild after plugin or native configuration changes; an OTA update cannot add or change native modules.

3. Initialize the SDK

Initialize once in your app’s entry point. This reads the native configuration and registers an already-authorized device without showing a new permission prompt.

App initialization
import Notirabbit from '@notirabbit/expo';

Notirabbit.initialize();

Call Notirabbit.requestPermission() when the user chooses to enable notifications. Receipt and click reporting are automatic.

Permission button
import { Alert, Button } from 'react-native';
import Notirabbit from '@notirabbit/expo';

export function EnableNotifications() {
  return <Button title="Enable notifications" onPress={() => {
    void Notirabbit.requestPermission().catch(() => {
      Alert.alert('Notifications', 'Could not enable notifications. Please try again.');
    });
  }} />;
}

4. Handle notification taps

Attach a listener in a mounted component so your navigation is ready. The SDK keeps the cold-start tap until the first listener subscribes. Validate the destination before passing it to your router.

Notification tap listener
import { useEffect } from 'react';
import { Linking } from 'react-native';
import Notirabbit from '@notirabbit/expo';

export function NotificationClicks() {
  useEffect(() => {
    const subscription = Notirabbit.addClickListener(({ url }) => {
      try {
        const destination = new URL(url);
        if (destination.protocol !== 'https:' ||
            destination.hostname !== 'news.example.com') return;
        void Linking.openURL(destination.href).catch(() => {
          // Show a navigation error in your app.
        });
      } catch {
        // Ignore malformed destinations.
      }
    });
    return () => subscription.remove();
  }, []);
  return null;
}

Mount this component once in your root layout and replace the example host with your own. For in-app routing, map the validated URL to your screen instead of calling Linking.openURL.

For a custom scheme, register it in both your app and App Settings → General → Mobile deep-link schemes. Send it using mobile_url. Otherwise, native notifications use the regular url.

5. Test on a device

  1. Install a native build and grant notification permission.
  2. Use getDeviceId() to identify your device, then send a selected-device test from the dashboard.
  3. Verify foreground, background and cold-start taps, plus receipt/click reporting.

If isSupported() returns false, check that you installed a native build with the package. For registration or delivery problems, check the platform identifier, credentials and OS permission. A blank device ID may mean registration is still pending; inspect getState().

Send to iOS and Android with the API

SDK reference

Expo SDK methods
MethodPurpose
requestPermission()Ask for permission and register or re-enable the device.
unsubscribe()Persist an opt-out until the user explicitly enables notifications again.
getState()Read device ID, subscribed, pending and opted-out state.
retryPending()Retry pending registration and retained engagement reports.
getDiagnostics()Read session counters without notification payloads or credentials.

More options and behavior notes: notirabbit/notirabbit-expo (repository access required).