Skip to main content

React Native Inbox SDK

Native React Native components for a fully-featured notification inbox with real-time updates, preference management, and built-in light/dark theme support.

Looking for the web version? See React Inbox for React DOM components with CSS styling.

Installation

npm install @zyphr-dev/inbox-react-native
# or
yarn add @zyphr-dev/inbox-react-native

Peer dependencies: react >= 17.0.0, react-native >= 0.64.0

Quick Start

1. Generate a Subscriber Token (Server-Side)

The subscriber token is a JWT generated on your backend using your API key. Never expose your API key to the client.

Your backend
app.get('/api/inbox-token', async (req, res) => {
const response = await fetch('https://api.zyphr.dev/v1/subscriber-inbox/token', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.ZYPHR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ external_id: req.user.id }),
});
const { data } = await response.json();
res.json({ token: data.token });
});

2. Add the Provider and Components

App.tsx
import { ZyphrProvider, InboxPopover } from '@zyphr-dev/inbox-react-native';

function App() {
const [token, setToken] = useState('');

useEffect(() => {
fetch('/api/inbox-token')
.then(r => r.json())
.then(d => setToken(d.token));
}, []);

if (!token) return null;

return (
<ZyphrProvider subscriberToken={token}>
<Navigation />
</ZyphrProvider>
);
}

No CSS import needed -- styling is handled via StyleSheet and the theme context.

Provider Configuration

<ZyphrProvider
subscriberToken={token} // Required
apiUrl="https://api.zyphr.dev" // Default
wsUrl="wss://ws.api.zyphr.dev" // Default
realtime={true} // Enable WebSocket updates (default: true)
theme="system" // 'light' | 'dark' | 'system' (default: 'system')
locale="en" // Locale for date formatting (default: 'en')
demo={false} // Demo mode with mock data (default: false)
>
{children}
</ZyphrProvider>
PropTypeDefaultDescription
subscriberTokenstring--Required. Subscriber JWT from /v1/subscriber-inbox/token
apiUrlstringhttps://api.zyphr.devAPI endpoint
wsUrlstringwss://ws.api.zyphr.devWebSocket endpoint
realtimebooleantrueEnable real-time WebSocket updates
theme'light' | 'dark' | 'system''system'Color scheme (uses RN Appearance API)
localestring'en'Locale for date formatting
demobooleanfalseRender with mock data, no API calls
Theme Detection

Unlike the web version, 'system' theme uses React Native's Appearance API, which works natively on both iOS and Android without DOM access.

Components

InboxBell

Bell icon button with unread count badge.

import { InboxBell } from '@zyphr-dev/inbox-react-native';

<InboxBell onPress={() => navigation.navigate('Inbox')} />
<InboxBell dot />
<InboxBell size={48} maxCount={50} />
PropTypeDefaultDescription
onPress() => void--Press handler
styleViewStyle--Container style
dotbooleanfalseShow dot instead of count
maxCountnumber99Maximum display count (e.g., 99+)
bellIconReactNode--Custom bell icon element
sizenumber40Button size in points

InboxPopover

Modal-based notification feed triggered by clicking the bell.

import { InboxPopover } from '@zyphr-dev/inbox-react-native';

// Uncontrolled (manages own visibility)
<InboxPopover />

// Controlled
<InboxPopover
visible={isOpen}
onVisibilityChange={setIsOpen}
animationType="fade"
/>

// Custom trigger
<InboxPopover trigger={<MyCustomButton />} />
PropTypeDefaultDescription
visibleboolean--Controlled visibility
onVisibilityChange(visible: boolean) => void--Visibility callback
styleViewStyle--Modal content style
categoriesstring[]--Filter by categories
triggerReactNode--Custom trigger element
animationType'slide' | 'fade' | 'none''slide'Modal animation
presentationStylestring'pageSheet'iOS modal presentation style

InboxFeed

Full notification list with All/Unread tabs, pull-to-refresh, and infinite scroll via FlatList.

import { InboxFeed } from '@zyphr-dev/inbox-react-native';

<InboxFeed />
<InboxFeed categories={['alerts', 'updates']} />
PropTypeDefaultDescription
categoriesstring[]--Filter by categories
styleViewStyle--Container style
showArchivedbooleanfalseInclude archived notifications
emptyStateReactNode--Custom empty state component
renderItem(notification, actions) => ReactNode--Custom notification renderer

NotificationItem

Renders a single notification with priority styling, relative timestamps, and action buttons.

import { NotificationItem } from '@zyphr-dev/inbox-react-native';

<NotificationItem
notification={notification}
onPress={() => handlePress(notification)}
/>

PreferencesPanel

ScrollView-based notification preference management with native Switch toggles.

import { PreferencesPanel } from '@zyphr-dev/inbox-react-native';

<PreferencesPanel
onSave={() => Alert.alert('Saved!')}
onError={(err) => Alert.alert('Error', err.message)}
/>
PropTypeDefaultDescription
styleViewStyle--Container style
showGlobalPreferencesbooleantrueShow global preferences section
channelLabelsRecord<string, string>--Custom channel labels
onSave() => void--Save success callback
onError(error: Error) => void--Error callback
saveButtonTextstring'Save Preferences'Custom button text
channelsNotificationChannel[]--Filter to specific channels

Hooks

All hooks share the same API as the web version -- only the import path changes.

useInbox

import { useInbox } from '@zyphr-dev/inbox-react-native';

function NotificationScreen() {
const {
notifications, unreadCount, isLoading, hasMore, error,
markAsRead, markAllAsRead, archive, delete: remove, loadMore, refresh,
} = useInbox();

return (
<FlatList
data={notifications}
renderItem={({ item }) => (
<Pressable onPress={() => markAsRead(item.id)}>
<Text style={{ fontWeight: item.read_at ? 'normal' : 'bold' }}>
{item.title}
</Text>
</Pressable>
)}
onEndReached={() => hasMore && loadMore()}
onRefresh={refresh}
refreshing={isLoading}
/>
);
}

useUnreadCount

import { useUnreadCount } from '@zyphr-dev/inbox-react-native';

function TabBadge() {
const { count } = useUnreadCount();
if (count === 0) return null;
return <View style={styles.badge}><Text>{count}</Text></View>;
}

useRealTime

import { useRealTime } from '@zyphr-dev/inbox-react-native';

function ConnectionIndicator() {
const { connected } = useRealTime();
return (
<View style={{
backgroundColor: connected ? '#22c55e' : '#ef4444',
width: 8, height: 8, borderRadius: 4,
}} />
);
}

usePreferences

import { usePreferences } from '@zyphr-dev/inbox-react-native';

function CustomPreferences() {
const { preferences, updatePreference, savePreferences, hasChanges } = usePreferences();
// Build your own preference UI using the data
}

Theming

Using the Theme Hook

Access the current theme anywhere inside the provider:

import { useZyphrTheme } from '@zyphr-dev/inbox-react-native';

function CustomComponent() {
const theme = useZyphrTheme();

return (
<View style={{ backgroundColor: theme.colors.background, padding: theme.spacing.lg }}>
<Text style={{ color: theme.colors.text, fontSize: theme.fontSize.md }}>
Styled with Zyphr theme
</Text>
</View>
);
}

Theme Structure

interface ZyphrTheme {
colors: {
primary, primaryHover, primarySubtle,
background, backgroundSecondary, backgroundHover,
text, textSecondary, textMuted,
border, borderSubtle,
danger, dangerHover, success, warning, urgent, unreadDot,
skeletonBase, skeletonShine,
};
spacing: { xs: 4, sm: 8, md: 12, lg: 16, xl: 24 };
borderRadius: { sm: 6, md: 10, lg: 14 };
fontSize: { xs: 11, sm: 13, md: 15, lg: 17, xl: 20 };
}

Accessing Color Palettes

import { LIGHT_COLORS, DARK_COLORS } from '@zyphr-dev/inbox-react-native';

Comparison with Web Version

FeatureWeb (@zyphr-dev/inbox-react)React Native (@zyphr-dev/inbox-react-native)
StylingCSS + CSS variablesStyleSheet + theme context
PopoverPositioned div + backdropModal component
Infinite scrollIntersectionObserverFlatList.onEndReached
Pull to refreshNot built-inFlatList.onRefresh
Linkswindow.open()Linking.openURL()
Theme detectionwindow.matchMedia()Appearance API
TogglesCustom CSS checkboxNative Switch
Focus trapDOM focus managementNative modal handles it
IconsInline SVGUnicode (customizable via props)
CSS importRequired (styles.css)Not needed

TypeScript

Full TypeScript support with exported types:

import type {
Notification,
ZyphrConfig,
ZyphrTheme,
ZyphrThemeColors,
UseInboxReturn,
UseUnreadCountReturn,
UseRealTimeReturn,
UsePreferencesReturn,
} from '@zyphr-dev/inbox-react-native';

Requirements

  • React Native 0.64+ (for Intl.RelativeTimeFormat support)
  • Expo SDK 52+ (if using Expo)
  • iOS 13+ / Android API 24+