In a React Native app, why does a remote notification's image show on Android but not on iOS, and how do local notifications attach media?
answer
- Android draws the image itself
- iOS needs a service extension
- a separate native target
- content.attachments, iOS only
- a file URL, or silently dropped
basics
~20 sReact Native Firebase's docs note Android shows an FCM notification image out of the box, while iOS needs a Notification Service Extension, a separate native target that fetches it. Local notifications attach media through expo-notifications' iOS-only content.attachments, using a file URL.
solid answer
~50 sThe two platforms render remote images differently. According to React Native Firebase's documentation, Android displays an FCM notification image with no extra setup, but iOS needs a **Notification Service Extension**: a separate native app-extension target that runs before the notification is shown, downloads the image and attaches it. That target is native code outside the React Native bundle, added in Xcode with the messaging pod linked to it, and in a prebuild-managed Expo project it would have to be generated by a config plugin. For **local** notifications, expo-notifications offers `content.attachments`, which is **iOS-only**: each attachment has a `url` plus optional `identifier`, `typeHint` and thumbnail options. iOS requires that URL to point to a file on the device; expo-notifications drops any attachment it cannot create without an error, so an `https` URL produces a reminder without the image. Its content input has no Android image field.
code
typescript · 12 linesimport * as Notifications from 'expo-notifications';
export async function scheduleReminderWithPhoto(localPhotoFileUrl: string) {
return Notifications.scheduleNotificationAsync({
content: {
title: 'Atorvastatin 20 mg',
body: 'Evening dose, one white tablet',
attachments: [{ identifier: 'pill', url: localPhotoFileUrl, type: null }],
},
trigger: { type: Notifications.SchedulableTriggerInputTypes.DAILY, hour: 21, minute: 0 },
});
}go deeper
Remember that iOS needs extra native setup to show images in pushes, and that local attachments in expo-notifications are iOS-only.
Explain the Notification Service Extension's role, why it ships only in a new binary, and why attachments need a file URL on the device.
Plan rich notifications as enhancement: generate the extension through a config plugin in prebuild projects, keep text self-sufficient, and test the silent-drop path.
Decide whether rich media justifies an extra native target to build, sign and maintain, against the plain-text reminders that already work on both platforms.
## Two platforms, two rendering models A notification with a picture, such as a photo of the pill in a medication reminder, is handled very differently by the two platforms React Native targets. | | Android | iOS | |---|---|---| | Remote (FCM) image | Displayed without extra setup, per React Native Firebase's docs | Needs a Notification Service Extension | | Local image via expo-notifications | No image field in the content input | `content.attachments` with a file URL | ## Remote images on iOS: the service extension iOS does not download images named in a push by itself. The app must ship a **Notification Service Extension**, a small separate native target that iOS runs when a remote notification arrives, before showing it. The extension downloads the image and attaches it to the notification content. React Native Firebase's guide sets it up by: 1. adding a Notification Service Extension target in Xcode; 2. linking the Firebase messaging pod to that target in the Podfile; 3. adding the extension code that attaches the image. Consequences for a React Native team: - The extension is **native code outside the JavaScript bundle**, so it ships only in a new binary; over-the-air updates cannot add or change it. - In an Expo project using prebuild, manual Xcode edits are regenerated away, so the target has to come from a config plugin. - The extension has a short time budget to download the image; large images should be avoided. ## Local images: expo-notifications attachments For notifications the app schedules itself, expo-notifications exposes `content.attachments`, documented as **iOS-only**. Each attachment is an object with: - `url` — the location of the media file; - `identifier` — an optional id; - `typeHint` — an optional hint for the file type; - `hideThumbnail`, `thumbnailClipArea`, `thumbnailTime` — thumbnail options. iOS builds each attachment from a URL that must point to a **file on the device**. expo-notifications creates attachments with a failure-tolerant call and discards any that fail, so an `https` URL, or a path to a file that is gone, produces a reminder without its image and no error. The practical flow is: 1. download or copy the image into the app's own storage; 2. pass that file URL in `attachments`; 3. schedule the notification. On Android, expo-notifications' content input has no attachment or image field, so a local reminder there shows text only. ## Preparing the local file The attachment's file has to exist before the notification is scheduled, and it must still exist when iOS processes the request: - copy or download the image into the app's own document or cache storage first; - use the resulting file URL, not the original remote address; - avoid deleting the file immediately after scheduling, since the notification is built from it; - keep images small, because they are stored with the notification. A reminder scheduled for next week therefore depends on the app's file handling as much as on the notification API. ## Designing for the difference For a medication reminder, the picture helps but must never be required: - put the medication name and dose in the title and body, so the reminder is complete without the image; - treat the image as progressive enhancement on iOS; - test on both platforms, since each fails differently. ## Where each piece lives - The **server** decides whether a push carries an image; its format belongs to the push service. - The **iOS extension** turns that image into an attachment for remote notifications. - The **React Native app** attaches images only to its own local notifications, through `content.attachments` on iOS. Keeping these apart helps when a bug report says "images are missing": the first question is whether the notification was remote or local, and on which platform. ## Common mistakes - Expecting iOS to show an FCM image because Android does. - Adding the service extension by hand in an Expo prebuild project and losing it on the next prebuild. - Passing a remote URL to `attachments` and assuming iOS downloads it. - Putting essential information only in the image.
- Why can't an over-the-air update fix missing notification images on iOS?The image is attached by a Notification Service Extension, a separate native target compiled into the app binary. Over-the-air updates replace only the JavaScript bundle and assets, so adding or changing the extension requires a new native build distributed through the store.
saying these in an interview costs you the question
- iOS downloads and shows an FCM notification image automatically, like Android
- The Notification Service Extension can be written in the React Native JavaScript bundle
- expo-notifications attachments work on Android too
- An https URL in attachments is downloaded by iOS before display
- A missing attachment makes scheduleNotificationAsync reject