RORK LABJP
TOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the roundTOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the round
Articles/Dev Tools
Dev Tools/2026-05-14Intermediate

Push Notifications Not Working on Android 13+ in Rork Apps — Adding POST_NOTIFICATIONS Permission

Push notifications silently failing on Android 13+? The culprit is usually a missing POST_NOTIFICATIONS permission. Here's how to add it to your Rork-generated app.

Android43Push Notifications9Permissions4POST_NOTIFICATIONSRork515Troubleshooting38

You've finished implementing push notifications, run every test you can think of — and then you hand the app to a friend with a newer Android phone and the notifications just... don't arrive.

On my own projects I keep hitting the same trap: everything works on iOS, yet the newest Android phones stay completely silent. The cause is almost always a missing Android 13 notification permission.

A notification that doesn't arrive is the same as no notification at all — and since it feeds straight into retention, this is one corner worth closing carefully.

Since Android 13 (API level 33), Google made a significant change: apps must now explicitly request permission from users before they can send notifications. This is a shift from the old opt-out model to an opt-in model — and it catches a lot of Rork-generated projects off guard.

What Changed in Android 13

On Android 12 and earlier, installing an app was enough. Unless the user actively went into settings and turned off notifications, they'd receive them by default.

Android 13 introduced a new runtime permission called POST_NOTIFICATIONS. Before your app can show any notification, it needs to ask the user:

Android 12 and earlier: Install → notifications work automatically
Android 13 and later:  Install → request permission → user approves → notifications work

This applies to devices running API level 33 or higher. Apps targeting API 32 or lower get a temporary exemption, but since Google Play now requires targetSdkVersion 34+, there's no way around implementing this properly.

Common Gaps in Rork-Generated Code

Rork uses expo-notifications under the hood. The generated code handles iOS permissions well, but Android 13's new requirement sometimes slips through. Here are the two patterns to look for:

Pattern 1: Permission request exists, but is iOS-centric with no Android-specific channel setup

// ❌ Works on iOS and Android 12 — silent failure on Android 13+
const { status } = await Notifications.requestPermissionsAsync();
if (status !== 'granted') {
  alert('Notifications permission required');
  return;
}

Pattern 2: app.json has no permissions array for Android

// ❌ Missing Android permissions declaration
{
  "expo": {
    "android": {
      "package": "com.yourapp.app"
    }
  }
}

Both issues are easy to miss because iOS simulators and older Android devices don't surface the problem.

Step 1: Add POST_NOTIFICATIONS to app.json

Open app.json and add the permission declaration under the android key:

// ✅ Android 13+ compatible app.json
{
  "expo": {
    "android": {
      "package": "com.yourapp.app",
      "permissions": [
        "android.permission.POST_NOTIFICATIONS",
        "android.permission.VIBRATE",
        "android.permission.RECEIVE_BOOT_COMPLETED"
      ]
    },
    "plugins": [
      [
        "expo-notifications",
        {
          "icon": "./assets/notification-icon.png",
          "color": "#ffffff"
        }
      ]
    ]
  }
}

VIBRATE and RECEIVE_BOOT_COMPLETED are also needed for reliable notification delivery, so add them while you're here.

Step 2: Update the Permission Request Code

Here's the corrected implementation that handles both iOS and Android 13+ properly:

// ✅ Handles Android 13+ POST_NOTIFICATIONS correctly
import * as Notifications from 'expo-notifications';
import { Platform } from 'react-native';
import Constants from 'expo-constants';
 
async function registerForPushNotificationsAsync() {
  // Android: create a notification channel first
  if (Platform.OS === 'android') {
    await Notifications.setNotificationChannelAsync('default', {
      name: 'Default',
      importance: Notifications.AndroidImportance.MAX,
      vibrationPattern: [0, 250, 250, 250],
      lightColor: '#FF231F7C',
    });
  }
 
  // Check current status before requesting
  const { status: existingStatus } = await Notifications.getPermissionsAsync();
  let finalStatus = existingStatus;
 
  // Only prompt if we haven't asked yet
  if (existingStatus !== 'granted') {
    const { status } = await Notifications.requestPermissionsAsync();
    finalStatus = status;
  }
 
  if (finalStatus !== 'granted') {
    console.warn('Push notification permission not granted');
    return null;
  }
 
  // SDK 52+ requires projectId
  const token = await Notifications.getExpoPushTokenAsync({
    projectId: Constants.expoConfig?.extra?.eas?.projectId,
  });
 
  return token.data;
}

The key pattern: always call getPermissionsAsync() first, then only call requestPermissionsAsync() if the status is undetermined. If the user has already granted or denied, the request dialog won't appear anyway — but checking first keeps your code predictable.

Step 3: Rebuild with EAS

This is a step that's easy to skip — app.json permission changes require a full EAS Build. They're compiled into AndroidManifest.xml, which means OTA updates via eas update won't apply them.

# Test with a preview build first
eas build --platform android --profile preview
 
# Once confirmed working, ship the production build
eas build --platform android --profile production

To test Android 13 behavior specifically, use an emulator running API level 33 or higher, or a physical device running Android 13+. Older emulators won't trigger the permission dialog, which makes it hard to confirm the fix is working.

Verifying the Fix

After installing the new build:

  1. Launch the app fresh — the "Allow notifications?" system dialog should appear
  2. Tap "Allow"
  3. Send a test notification from Expo's Push Notification Tool
  4. Confirm the notification arrives on the device

You can also verify the grant state directly with adb instead of guessing from the dialog — reading the OS-side state makes isolating the cause much faster.

# Check the notification permission grant state (granted=true means allowed)
adb shell dumpsys notification_manager | grep -A1 com.yourapp.app
 
# List the permissions declared for the package (is POST_NOTIFICATIONS there?)
adb shell dumpsys package com.yourapp.app | grep POST_NOTIFICATIONS

If the second command shows nothing, it's a sign your app.json change never made it into the build — revisit the EAS Build step.

If the dialog doesn't appear, you can manually enable notifications in Settings → Apps → [Your App] → Notifications to continue testing. But always fix the code — real users won't dig through settings.

A Note on Expo SDK Versions

The projectId parameter in getExpoPushTokenAsync() became required in Expo SDK 52. Without it, you might see token retrieval succeed in development builds but fail in production — which is a frustrating bug to track down.

// If you're on SDK 52+, this is required
const token = await Notifications.getExpoPushTokenAsync({
  projectId: Constants.expoConfig?.extra?.eas?.projectId,
});

If your Rork project was generated on an older template, check whether this parameter is present.


Android's notification permission model will likely keep evolving. POST_NOTIFICATIONS is the current requirement, but staying current with Expo SDK release notes and Google Play policy changes is a worthwhile habit — especially in areas like notifications, payments, and device permissions where the rules change most often.

For related issues, see Push Notifications Not Delivering in Rork Apps and EAS OTA Updates Not Reflecting on Device.

Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Dev Tools2026-04-29
Why Your Rork Android App Shows a White Square Notification Icon (and How to Fix It)
Your Rork or Expo app's notification icon shows up as a white square or blob on Android. Here's the underlying Android spec, the correct transparent icon recipe, the right app.json fields, and the cache traps that make fixes appear to do nothing.
Dev Tools2026-05-29
Diagnosing 'Network request failed' That Only Hits Android Emulator in Rork
Your fetch returns fine in the iOS simulator but throws 'Network request failed' the moment you switch to Android. Here is the diagnosis order I use to separate localhost, cleartext, certificate, and proxy issues, with code that actually compiles.
Dev Tools2026-05-19
Fixing 'Row too big to fit into CursorWindow' on Android When AsyncStorage Holds Too Much in Rork
When a React Native app generated with Rork stores large JSON or image metadata in AsyncStorage, Android can throw a Row too big to fit into CursorWindow exception. Here are the practical fixes — MMKV migration, chunked keys, payload trimming, and compression — explained from real wallpaper-app experience.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →