Add a bell with an unread dot and a full-screen inbox that shows one subscriber's notifications.
Premium has no inbox
On Premium, every inbox call answers 201 with an explanation, so the SDK shows an empty inbox and an unread count of 0. Notifications sent meanwhile are kept and appear after an upgrade. See what each plan includes.
Add the bell
Put NotificationInboxBell in your header with mode="indie" and the subscriber's id:
import { NotificationInboxBell } from 'native-notify';
// Expo Router / React Navigation header:
<Stack.Screen
name="index"
options={{
headerRight: () => (
<NotificationInboxBell
appId={yourAppId}
appToken="yourAppToken"
mode="indie" // or "mass" (default)
subId={currentUser.id} // required when mode="indie"
/>
),
}}
/>
subId is the id you registered with registerIndieID. appId and appToken are optional once you set them with NativeNotify.init() or <NativeNotifyProvider>.
Indie mode
- Each row has a delete button that calls
deleteIndieNotificationInbox. The row is removed at once and restored if the request fails. Hide the button withallowDelete={false}. - Opening the inbox marks every notification of that
subIdas read, not only the fetched page, so the dot clears. To keep a read state per row, useperNotificationRead. - It works in Expo Go, on simulators and on the web, because read state is per
subId. Mass mode needs a real device for its push token.
Per-notification read state
Needs native-notify v5.2 or later. Pass perNotificationRead to keep each row's own read state instead of marking the whole inbox read on open:
<NotificationInboxBell
appId={yourAppId}
appToken="yourAppToken"
mode="indie"
subId={currentUser.id}
perNotificationRead
/>
- Rows load with their real
readflag, and fetching marks nothing read. - Unread rows show a dot. Tapping a row marks it read and refreshes the unread count.
- The unread count comes from the server instead of being zeroed when the inbox opens.
Props
| Prop | Type | Default | Notes |
|---|---|---|---|
appId | number | string | — | Your app id. |
appToken | string | — | Your app token. |
mode | 'mass' | 'indie' | 'mass' | Use 'indie' for per-subscriber notifications. |
subId | number | string | — | Required when mode="indie". |
take | number | 20 | Notifications fetched per page. |
syncBadge | boolean | true | Sync the unread count to the app icon badge with Notifications.setBadgeCountAsync(). |
perNotificationRead | boolean | false | Per-row unread dots. See Per-notification read state. |
colors | object | light or dark theme | Partial theme override. See Colors. |
title | string | 'Notifications' | Inbox header title. |
emptyText | string | "You're all caught up" | Empty-state text. |
allowDelete | boolean | true | Show the per-row delete button (indie mode only). |
onNotificationPress | (notification) => void | — | Called when a row is tapped. |
onOpen | () => void | — | Called instead of opening the built-in inbox screen. |
showCount | boolean | false | Show a numeric badge instead of a plain dot. |
maxCount | number | 99 | Badge cap. A larger count shows as 99+. |
renderIcon | ({ unreadCount, color }) => node | bundled bell icon | Custom bell icon. |
iconSize | number | 24 | Bell icon size. |
iconStyle | StyleProp<ImageStyle> | — | Style override for the bell icon. |
containerStyle | StyleProp<ViewStyle> | — | Style override for the bell container. |
Colors
Colors follow the device's light or dark mode. Pass any of these keys in colors to override the theme:
<NotificationInboxBell
appId={yourAppId}
appToken="yourAppToken"
mode="indie"
subId={currentUser.id}
colors={{ dot: '#EF4444', accent: '#2563EB' }}
/>
Keys: icon, dot, badgeText · background, headerBackground, card, border · title, text, mutedText, emptyTitle, emptyText · accent, delete.
Custom screen and hook
To use your own trigger, render <NotificationInboxScreen> with visible and onClose. It takes the props above except the bell-only ones (onOpen, showCount, maxCount, renderIcon, iconSize, iconStyle, containerStyle), plus:
| Prop | Type | Default | Notes |
|---|---|---|---|
visible | boolean | — | Whether the inbox modal is shown. |
onClose | () => void | — | Called when the modal is dismissed. |
For a fully custom UI, use the headless hook:
import { useNotificationInbox } from 'native-notify';
const {
notifications, // fetched inbox rows
unreadCount, // unread count for your own badge
loading, // initial load in progress
refreshing, // pull-to-refresh in progress
loadingMore, // next page in progress
hasMore, // more pages available
error, // error message, if any
openInbox, // loads page 1 — and marks the whole inbox read, unless perNotificationRead
refresh,
refreshUnread,
loadMore,
deleteNotification, // per-row delete (indie mode)
perNotificationRead, // true when you built the hook with the option
markNotificationRead, // mark ONE row read when it is opened (per-notification mode)
} = useNotificationInbox({
appId: yourAppId,
appToken: 'yourAppToken',
mode: 'indie',
subId: currentUser.id, // required when mode: 'indie'
take: 20,
syncBadge: true, // sync the unread count to the app icon badge
});
The hook fetches only the unread count on mount. Call openInbox() or refresh() to load rows. markNotificationRead takes the row object, not an id. For an exact "load more", use getIndieNotificationInboxPage().
Row data
Rows have the shape getIndieNotificationInbox returns:
{
notification_id, // unique notification ID — used for deletes
date, // notification date
title, // notification title
message, // notification message
pushData, // the pushData you sent, as a JSON string — JSON.parse(row.pushData)
read, // this subscriber's read state — authoritative with perNotificationRead
}