React Native SDK
@notificationstream/react-native: provider, hooks, and a native bell, drawer sheet and inbox feed.
React Native components and hooks: a bell with a badge, a slide-in drawer sheet, an inbox feed with pull-to-refresh and infinite scroll, and headless hooks. Built on the JavaScript SDK: its client and its headless UI core (item view models, click flow, feed controller), so behaviour matches the web SDKs. Styles stay native and derive from the same resolved theme.
- No native modules and no
react-native-svg: the bell icon is drawn with Views (pass your owniconif you prefer). - Refetches when the app becomes active again; with
useRealtime()orusePolling(), stays up to date only while the app is in the foreground (AppState). - Optional offline cache through any AsyncStorage-compatible
storage; not a hard dependency. - Dark mode via
useColorScheme, theming viaThemeConfigand per-elementstyles.
Install
pnpm add @notificationstream/react-native
# optional: offline cache across launches
pnpm add @react-native-async-storage/async-storagePeer dependencies: react 18 or later, react-native 0.72 or later.
Quick start
import AsyncStorage from "@react-native-async-storage/async-storage";
import {
NotificationBell,
NotificationProvider,
usePolling,
useRealtime,
} from "@notificationstream/react-native";
export function App({ user }) {
return (
<NotificationProvider
publishableKey="pk_…"
user={{ id: user.id, name: user.name }}
token={fetchNotificationToken} // gets a user token from your server
storage={AsyncStorage}
>
<LiveUpdates />
<Header right={<NotificationBell />} />
{/* … */}
</NotificationProvider>
);
}
// Optional: update as soon as the feed changes (Growth plan and up), and poll every
// minute while realtime isn't connected.
function LiveUpdates() {
useRealtime();
usePolling(60_000);
return null;
}fetchNotificationToken calls an endpoint on your backend that signs a user token for the
signed-in user and returns it as text. With a function, the client gets a new token whenever the old one is about to expire, which suits
apps that stay open. See Identity verification.
<NotificationProvider>
The same props as the React provider:
publishableKey, user, token, apiUrl, locale, theme, labels,
client, pageSize, fetch, WebSocket, deliverOnStart, plus:
| Prop | Type | |
|---|---|---|
storage | StorageAdapter | null | AsyncStorage-compatible (getItem/setItem/removeItem, sync or async). Enables stale-while-revalidate across launches, and remembers the last synced profile. |
platform | PlatformAdapter | Default createReactNativePlatform(storage). |
Changing user (compared by value) or a string token switches user without remounting;
a new token function on each render doesn't. Changing publishableKey, apiUrl, locale
or pageSize creates a new client.
Hooks
The same as in React: useNotifications(), useUnreadCount(),
useUnseenCount(), useNotificationClient(),
useNotificationSelector(selector, isEqual?), usePresentedNotification({ filter?, enabled? }),
useNotificationArrival(listener, { filter?, enabled? }), useRealtime(enabled?) and
usePolling(interval | null).
useRealtime() keeps a realtime connection open while mounted and the app is in the
foreground; usePolling(interval) polls meanwhile and pauses while realtime is connected.
To open a notification as a bottom sheet, modal or screen when it arrives, only on the
screens you choose, or on app start (deliverOnStart), see
Presenting new notifications. Pass useIsFocused()
as enabled so screens kept mounted in a stack don't present in the background.
Components
Shared props: theme?: ThemeConfig, labels?: Partial<NotificationLabels>,
styles?: Partial<NotificationStyles>.
<NotificationBell />
A Pressable with an unseen-count badge. It opens <NotificationDrawer> unless you pass
onPress (for example to navigate to your own inbox screen).
| Prop | Type | Default |
|---|---|---|
icon | ReactNode | <BellGlyph /> (drawn with Views) |
color / size | string / number | theme foreground / 22 |
renderBadge | ({ unseenCount, unreadCount }) => ReactNode | |
maxBadgeCount | number | 99 |
open / onOpenChange | uncontrolled by default | |
onPress | () => void | opens the drawer |
all NotificationDrawer and NotificationFeed props |
<NotificationDrawer open onOpenChange />
A transparent Modal with a sheet that slides in from side ("right" by default, or
"left"). Tapping the overlay or pressing Android back closes it. Opening it marks
everything as seen. Pass safe-area insets with insets={{ top, bottom }} (for example from
react-native-safe-area-context). Default tabs: All / Unread.
<NotificationFeed />
A FlatList inbox: tabs (default All / Unread / Archived), pull-to-refresh
(RefreshControl), onEndReached infinite scroll, "Mark all as read", and loading, empty
and error states. Props: tabs, renderItem(item), renderEmpty(status), headerRight,
hideHeader, onNotificationClick, formatTime, showArchive, showReadToggle.
Use it as a full inbox screen:
function InboxScreen() {
return <NotificationFeed />;
}<NotificationItem item={…} />
A row with avatar (imageUrl or initial), title (bold while unread), Markdown-subset body
(bold, italic, tappable links), relative time, an action button, and visible "Mark as read"
and "Archive" buttons (no swipe gestures).
Tapping a row tracks the click and opens actionUrl with Linking.openURL, unless
onNotificationClick calls event.preventDefault() or returns false. Absolute URLs,
including app deep links such as myapp://inbox, are opened; javascript:, data:,
vbscript: and file: URLs never are. Relative URLs (/path) are never passed to
Linking: handle them in onNotificationClick with your
navigator, or send navigation targets in data:
<NotificationFeed
onNotificationClick={(item, event) => {
if (item.data?.screen) {
event.preventDefault();
navigation.navigate(String(item.data.screen));
}
}}
/>Theming
Precedence: built-in defaults → dashboard theme → the client's theme option → provider
theme → component theme.
colorScheme: "system" (the default) follows useColorScheme(). Override individual
elements with styles:
<NotificationFeed styles={{ title: { fontSize: 24 }, itemUnread: { backgroundColor: "#fff7ed" } }} />Style keys: bell, badge, badgeText, overlay, sheet, container, header,
title, headerButton, headerButtonText, tabs, tab, tabActive, tabText,
tabTextActive, item, itemUnread, avatar, avatarFallback, avatarText, content,
itemTitle, itemTitleUnread, itemBody, link, time, dot, actionButton,
actionButtonText, itemActions, iconButton, iconButtonText, empty, emptyText,
footer.
createNotificationStyles(resolvedTheme) returns the default stylesheet if you build your
own components.
Other exports
| Export | |
|---|---|
createReactNativePlatform(storage?) | The AppState platform adapter, for use with createNotificationClient directly. |
BellGlyph({ color, size }) | The View-drawn bell icon. |
openUrl(url) | The default Linking opener (absolute URLs only, never script, data or file URLs). |