◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Beta since 09/15 with no stable date yet; running projects in Expo Go has required login since 09/03●WIDGET — A write-up traces an iOS widget that renders black only in production builds●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Beta since 09/15 with no stable date yet; running projects in Expo Go has required login since 09/03●WIDGET — A write-up traces an iOS widget that renders black only in production builds●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/Dev Tools
⬡ Dev Tools/2026-04-29Intermediate

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.

Rork585Android55Push Notifications9Expo219Troubleshooting43

You ship an app built with Rork. iOS notifications look perfect — full-color icon, brand intact. Then you sideload the Android build and a notification arrives looking like a white blob in the status bar. No logo. No colors. Just a vague white shape.

I've hit this on every Android app I've shipped through Rork, and the fix is genuinely small once you understand what Android is actually doing. The frustrating part is that it touches three layers at once — the PNG itself, your app.json, and Android's resource cache — and most "fix it" articles only cover one of them. This guide walks through all three, plus the verification steps I use to make sure the change actually shipped.

What Android actually does to your notification icon

Starting with Android 5.0 (Lollipop), the small icon that shows up in the status bar and notification shade is required to be a white silhouette on a transparent background. When the system receives a colored PNG, it does not display it as-is. It strips every color channel and replaces every non-transparent pixel with white. A solid colored icon becomes a solid white rectangle. A gradient becomes a foggy white blob. This is by design — Material Design requires the small icon to match the system's monochrome convention.

This is not a Rork bug or an Expo bug. It is the Android OS spec. iOS uses your color icon directly for notifications, which is exactly why the issue stays hidden if you only test on simulators or TestFlight. The first time it shows up is usually when a real Android device receives your first push — by which point the build is already in the Play Store.

The right way to make a notification icon

The small icon needs to meet four requirements:

  • Fully transparent PNG — the background must have alpha = 0, not white pixels.
  • Pure white shape — #FFFFFF solid fill, no semi-transparent grays, no gradients.
  • At least 96×96 px — 256×256 px is a safer target so it scales cleanly to all densities.
  • 12–15% padding around the shape — Android crops the edges, so don't draw to the bezel.

The hardest discipline here is throwing color information away. If you start from your colored app icon in Photoshop or Figma, don't just recolor the layer to white. Extract the silhouette, refill it as solid white, and discard the original color layers. Drop shadows and gradients leave anti-aliased gray pixels that Android treats as transparent — the result is a fuzzy outline instead of a crisp shape.

Configuring it in your Rork project

Rork projects are Expo SDK based. The notification icon belongs in your app.json (or app.config.js) under both the Android section and the notification section.

{
  "expo": {
    "android": {
      "package": "com.example.myapp",
      "icon": "./assets/icon.png",
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#FFFFFF"
      }
    },
    "notification": {
      "icon": "./assets/notification-icon.png",
      "color": "#FF6B6B",
      "androidMode": "default"
    }
  }
}

The single most common mistake is treating expo.android.icon and expo.notification.icon as the same thing. They aren't. The first is your launcher icon (where color is welcome). The second is your status bar small icon (where color will be discarded). They must point to different files, or your "fix" won't be a fix at all.

expo.notification.color defines the accent color the system paints behind your white silhouette on Android 5.0+. Set it to your brand color and your notifications stop looking like generic system messages. Leave it out and Android falls back to a muted gray.

If you use expo-notifications for local or remote notifications, no extra wiring is needed. The library reads expo.notification.icon directly.

Five reasons your fix isn't taking effect

Even after the changes above, the white square sometimes refuses to die. In my experience the cause is almost always one of these.

1. The build cache still has the old resources. Editing app.json doesn't repackage your existing APK or AAB. You need a fresh EAS Build, and you need to uninstall the app from the device before reinstalling. OTA updates do not touch native resources.

2. The PNG isn't actually transparent. This trap catches more developers than any other. A PNG saved with a flat white "Background" layer in Photoshop, exported as PNG-8, or renamed from a JPEG will look transparent in your file browser but have no alpha channel. Verify with pngcheck:

# Confirm the PNG has real transparency
pngcheck -v assets/notification-icon.png
# Look for "tRNS" or "alpha channel" in the output — if absent, your icon is opaque

3. The asset path doesn't resolve. ./assets/notification-icon.png is relative to your app.json location, not the project root. Some Rork project layouts put assets one folder deeper. Watch the build log for Icon not found warnings; they are easy to skim past.

4. Android is caching the old icon at the OS level. Repeated installs can leave stale resources cached. The reliable sequence is: uninstall the app, restart the device, install again. I always reboot the device when validating a new icon — it eliminates a whole class of false negatives.

5. The push payload is overriding your config. If your FCM payload sets notification.icon directly, it wins over app.json. Check your Cloudflare Workers / Firebase Functions / backend code for any explicit icon field and remove it. Centralizing the icon in app.json is much easier to maintain.

Practical tips for designing the silhouette

I keep a single Figma template I reuse across every project. A 256×256 frame, all shapes filled with solid white, no shadows, no gradients. Export with Background set to Transparent and the resulting PNG is ready to drop into assets/notification-icon.png.

In Photoshop, the workflow I use is: load the colored app icon's selection, fill with white on a new layer, delete the background layer, then File → Export → Quick Export as PNG with "Preserve Transparency" enabled. Always double-check that last checkbox — it is the difference between a working icon and a white square.

For complex logos, the silhouette alone often becomes unrecognizable. The honest fix is to redesign for this constraint: pick one or two distinctive shape elements and let the rest go. If your logo includes text, use a heavy weight — thin strokes will collapse at the small status bar size.

If your real problem turns out to be the launcher icon failing to refresh after a rebuild, see Rork で作ったアプリのアイコンを更新したのに反映されない問題の解決法. If your notifications are not arriving at all (separate from the icon issue), Rork のプッシュ通知が届かないときの原因切り分けガイド is the better starting point. And for a full OneSignal integration walkthrough, Rork × OneSignal でプッシュ通知を実装する完全ガイド covers the end-to-end setup.

How to verify it actually works

Install a dev client or preview build on a physical Android device — not Expo Go. Expo Go ships with its own icon, which is what you'll see if you test through it, masking whether your config is right.

Drop this into a debug screen to fire a test notification:

import * as Notifications from 'expo-notifications';
 
// Schedule a 5-second local notification so you can swipe to home and inspect the status bar
export async function fireTestNotification() {
  await Notifications.scheduleNotificationAsync({
    content: {
      title: 'Icon check',
      body: 'If the status bar icon is a clean white silhouette, you are done.',
    },
    trigger: { seconds: 5 },
  });
}

Wire this to a button, tap it, switch to the home screen within five seconds, and watch the status bar. The expected outcome is a crisp white silhouette plus your title and body in both the status bar and the notification drawer. If you still see a square, go back to the PNG alpha channel and the app.json path — those two cover the vast majority of remaining cases.

For absolute certainty that your build picked up the new asset, you can unzip the APK and confirm res/drawable-*/notification_icon.png is present and looks correct:

unzip -p app.apk res/drawable-xxxhdpi-v4/notification_icon.png > /tmp/check.png
pngcheck -v /tmp/check.png

Wrapping up

Once you internalize Android's monochrome small-icon spec, you stop tripping over this for the rest of your career. The single concrete next step I'd suggest: open your current project's app.json and check whether expo.notification.icon is set, and whether it points to a different file from expo.android.icon. If it's missing or pointing to your color icon, you already know what to ship next.

For new projects, build the launcher icon and the notification icon together as a single deliverable. It saves you a rebuild and a re-submission a few weeks down the road, and it forces you to confirm your brand still reads at notification scale — which is a useful design exercise either way.

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-10-15
When EXPO_PUBLIC_ Values Read Fine in Dev but Come Back undefined in Production: Telling the Spellings Apart
The API URL reads fine while developing and turns undefined once the app is built. The cause was how process.env was written. I lined up babel-preset-expo's output for dev and production and tabulated which spellings get replaced and which are left behind.
⬡ Dev Tools2026-10-07
Japanese Headings Won't Turn Bold on Android: How to Trace and Fix a fontWeight That Does Nothing
A troubleshooting note for Rork apps where headings look bold on iPhone but stay thin on Android. How to tell the symptoms apart, pick weights through fontFamily, and verify the fix.
⬡ Dev Tools2026-09-21
A build that stops with exit code 0 — the flag that silenced the log, and the check that let an empty file through
Two reports landed in the same week: eas build --local stopping with exit code 0 but produced no further output, and a zero-byte stub that never got embedded. Both came down to settings that reduce output. Here is how to audit your own scripts.