Skip to main content

React Inbox SDK (Web)

Drop-in React components for a fully-featured notification inbox with real-time updates, preference management, and CSS theme support.

Building a React Native app? See React Native Inbox for native mobile components with StyleSheet theming.

Live Preview

Try the inbox components below — click the bell icon, mark notifications as read, archive, and explore the different views.

Installation

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

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
// Express example
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 }); // data.expires_in = 604800 (7 days)
});

2. Add the Provider and Components

Your frontend
import { ZyphrProvider, InboxBell, InboxPopover } from '@zyphr-dev/inbox-react';
import '@zyphr-dev/inbox-react/styles.css';

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}>
<InboxBell />
<InboxPopover />
</ZyphrProvider>
);
}

Provider Configuration

<ZyphrProvider
subscriberToken={token} // Required — JWT from POST /v1/subscriber-inbox/token
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
subscriberTokenstringRequired. 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 ('system' auto-detects, web only)
localestring'en'Locale for date formatting
demobooleanfalseRender with mock data, no API calls

Demo Mode

Use demo mode for previews and testing without API calls:

<ZyphrProvider subscriberToken="demo" demo>
<InboxBell />
<InboxPopover />
</ZyphrProvider>

Components

InboxBell

Bell icon with unread badge count.

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

<InboxBell />

InboxPopover

Dropdown notification feed triggered by clicking the bell.

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

<InboxPopover />

InboxFeed

Full notification list with All/Unread tabs, infinite scroll, and mark-all-as-read.

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

<InboxFeed />
PropTypeDefaultDescription
renderItem(notification, actions) => ReactNodeCustom notification renderer
classNamestringCSS class

NotificationItem

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

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

<NotificationItem
notification={notification}
onClick={() => handleClick(notification)}
/>

PreferencesPanel

Notification preference management UI.

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

<PreferencesPanel />

Hooks

useInbox

Access notifications and actions:

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

function CustomInbox() {
const {
notifications, // Notification[] — always an array, never undefined
unreadCount, // number
isLoading, // boolean
hasMore, // boolean — more pages available
error, // Error | null

markAsRead, // (id: string) => Promise<void>
markAllAsRead, // () => Promise<void>
archiveNotification, // (id: string) => Promise<void>
deleteNotification, // (id: string) => Promise<void>
loadMore, // () => Promise<void> — load next page
refreshNotifications, // () => Promise<void> — refresh from start
} = useInbox();

return (
<div>
<button onClick={markAllAsRead}>Mark all read</button>
{notifications.map(n => (
<div key={n.id} onClick={() => markAsRead(n.id)}>
<h3>{n.title}</h3>
<p>{n.body}</p>
</div>
))}
{hasMore && <button onClick={loadMore}>Load more</button>}
</div>
);
}

useUnreadCount

Get just the unread count:

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

function NavBar() {
const { unreadCount } = useUnreadCount();

return (
<nav>
<a href="/inbox">
Inbox {unreadCount > 0 && `(${unreadCount})`}
</a>
</nav>
);
}

useRealTime

WebSocket connection status:

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

function ConnectionStatus() {
const { connected } = useRealTime();
return <span>{connected ? 'Live' : 'Reconnecting...'}</span>;
}

usePreferences

Manage notification preferences:

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

function PreferencesPage() {
const { preferences, isLoading, updatePreferences } = usePreferences();

if (isLoading) return <Spinner />;

return (
<div>
{preferences?.categories.map(cat => (
<div key={cat.category.id}>
<h3>{cat.category.name}</h3>
{cat.channels.map(ch => (
<label key={ch.channel}>
<input
type="checkbox"
checked={ch.enabled}
onChange={() => updatePreferences([{
category_id: cat.category.id,
channel: ch.channel,
enabled: !ch.enabled,
}])}
/>
{ch.channel}
</label>
))}
</div>
))}
</div>
);
}

Styling

CSS Variables

:root {
--zyphr-primary: #4a90d9;
--zyphr-background: #ffffff;
--zyphr-text: #1a1a1a;
--zyphr-border: #e5e5e5;
--zyphr-unread: #e8f4fd;
--zyphr-badge: #ef4444;
}

React Native

For React Native apps, use the dedicated @zyphr-dev/inbox-react-native package which provides native components built with View, FlatList, Modal, Switch, and React Native's Appearance API.

npm install @zyphr-dev/inbox-react-native

See the React Native Inbox docs for full component API, theming, and usage examples.

Server-Side Rendering

The provider handles SSR gracefully — no DOM access during server render:

Next.js App Router
'use client';

import { ZyphrProvider, InboxBell, InboxPopover } from '@zyphr-dev/inbox-react';
import '@zyphr-dev/inbox-react/styles.css';

export function Inbox({ subscriberToken }: { subscriberToken: string }) {
return (
<ZyphrProvider subscriberToken={subscriberToken}>
<InboxBell />
<InboxPopover />
</ZyphrProvider>
);
}

TypeScript

Full TypeScript support with exported types:

import type {
Notification,
ZyphrConfig,
InboxState,
UseInboxReturn,
UseUnreadCountReturn,
} from '@zyphr-dev/inbox-react';

Requirements

  • React 18.0 or later
  • Modern browser (Chrome, Firefox, Safari, Edge) for web components
  • React Native (Expo SDK 52+) for hooks-only usage