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 own icon if you prefer).
  • Refetches when the app becomes active again; with useRealtime() or usePolling(), 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 via ThemeConfig and per-element styles.

Install

pnpm add @notificationstream/react-native
# optional: offline cache across launches
pnpm add @react-native-async-storage/async-storage

Peer 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:

PropType
storageStorageAdapter | nullAsyncStorage-compatible (getItem/setItem/removeItem, sync or async). Enables stale-while-revalidate across launches, and remembers the last synced profile.
platformPlatformAdapterDefault 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).

PropTypeDefault
iconReactNode<BellGlyph /> (drawn with Views)
color / sizestring / numbertheme foreground / 22
renderBadge({ unseenCount, unreadCount }) => ReactNode
maxBadgeCountnumber99
open / onOpenChangeuncontrolled by default
onPress() => voidopens 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).