React Inbox SDK
A drop-in notification inbox component for React applications with real-time updates.
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/inbox-react
# or
yarn add @zyphr/inbox-react
Quick Start
Your public key is available in the Dashboard under Applications → [Your App] → API Keys. It starts with za_pub_ and is safe to use in client-side code.
import { ZyphrInbox, ZyphrProvider } from '@zyphr/inbox-react';
function App() {
return (
<ZyphrProvider
publicKey="za_pub_your_key"
subscriberId="user_123"
>
<YourApp />
<ZyphrInbox />
</ZyphrProvider>
);
}
Provider Configuration
Wrap your app with ZyphrProvider:
<ZyphrProvider
// Required
publicKey="za_pub_your_key"
subscriberId="user_123"
// Authentication (choose one)
token="subscriber_jwt_token" // Static token
tokenEndpoint="/api/inbox-token" // Fetch token from endpoint
// Optional
baseUrl="https://api.zyphr.dev"
wsEndpoint="wss://realtime.zyphr.dev"
debug={false}
>
{children}
</ZyphrProvider>
Demo Mode
Use demo mode to render with mock data and no API or WebSocket calls — useful for previews, documentation, and testing:
<ZyphrProvider subscriberToken="demo" demo>
<ZyphrInbox />
</ZyphrProvider>
Token Authentication
For secure authentication, generate tokens server-side:
// Express endpoint
app.get('/api/inbox-token', async (req, res) => {
const token = await zyphr.inbox.createToken({
subscriberId: req.user.id,
});
res.json({ token });
});
<ZyphrProvider
publicKey="za_pub_xxx"
subscriberId={user.id}
tokenEndpoint="/api/inbox-token"
>
Inbox Component
Basic Usage
<ZyphrInbox />
With Options
<ZyphrInbox
// Display
position="bottom-right" // "top-left" | "top-right" | "bottom-left" | "bottom-right"
theme="auto" // "light" | "dark" | "auto"
showUnreadBadge={true}
badgePosition="top-right"
// Behavior
playSound={true}
showToast={true}
closeOnOutsideClick={true}
// Limits
pageSize={25}
maxHeight="500px"
// Callbacks
onNotificationClick={(notification) => {
router.push(notification.actionUrl);
}}
onUnreadCountChange={(count) => {
document.title = count > 0 ? `(${count}) App` : 'App';
}}
onOpen={() => console.log('Inbox opened')}
onClose={() => console.log('Inbox closed')}
/>
Bell Icon
Use the default bell or your own trigger:
Default Bell
<ZyphrInbox />
Custom Trigger
<ZyphrInbox
renderTrigger={({ unreadCount, onClick }) => (
<button onClick={onClick} className="my-bell">
<BellIcon />
{unreadCount > 0 && <span>{unreadCount}</span>}
</button>
)}
/>
Hooks
useZyphrInbox
Access inbox state and methods:
import { useZyphrInbox } from '@zyphr/inbox-react';
function CustomInbox() {
const {
// State
notifications,
unreadCount,
isLoading,
hasMore,
// Actions
markAsRead,
markAllAsRead,
archive,
delete: deleteNotification,
refresh,
loadMore,
} = useZyphrInbox();
return (
<div>
<button onClick={markAllAsRead}>Mark all read</button>
{notifications.map(n => (
<NotificationItem
key={n.id}
notification={n}
onRead={() => markAsRead(n.id)}
/>
))}
{hasMore && <button onClick={loadMore}>Load more</button>}
</div>
);
}
useZyphrEvents
Listen for real-time events:
import { useZyphrEvents } from '@zyphr/inbox-react';
function NotificationToast() {
useZyphrEvents({
onNotification: (notification) => {
toast.success(notification.title);
},
onUnreadCountChange: (count) => {
// Update favicon badge, etc.
},
onConnectionChange: (connected) => {
if (!connected) {
console.warn('Realtime disconnected');
}
},
});
return null;
}
useUnreadCount
Get just the unread count:
import { useUnreadCount } from '@zyphr/inbox-react';
function NavBar() {
const unreadCount = useUnreadCount();
return (
<nav>
<a href="/inbox">
Inbox {unreadCount > 0 && `(${unreadCount})`}
</a>
</nav>
);
}
Preferences Component
Let users manage their notification preferences:
import { ZyphrPreferences } from '@zyphr/inbox-react';
function SettingsPage() {
return (
<ZyphrPreferences
categories={[
{ id: 'comments', label: 'Comments', description: 'New comments on your posts' },
{ id: 'mentions', label: 'Mentions', description: 'When someone mentions you' },
{ id: 'updates', label: 'Product Updates', description: 'New features and improvements' },
]}
onSave={(preferences) => {
console.log('Saved:', preferences);
}}
/>
);
}
Styling
CSS Variables
:root {
/* Colors */
--zyphr-primary: #4a90d9;
--zyphr-primary-hover: #3a7bc8;
--zyphr-background: #ffffff;
--zyphr-surface: #f5f5f5;
--zyphr-text: #1a1a1a;
--zyphr-text-secondary: #666666;
--zyphr-border: #e5e5e5;
/* Unread indicator */
--zyphr-unread-bg: #e8f4fd;
--zyphr-unread-dot: #4a90d9;
/* Badge */
--zyphr-badge-bg: #ef4444;
--zyphr-badge-text: #ffffff;
/* Sizing */
--zyphr-border-radius: 8px;
--zyphr-font-size: 14px;
}
/* Dark mode */
[data-theme="dark"] {
--zyphr-background: #1a1a1a;
--zyphr-surface: #2a2a2a;
--zyphr-text: #ffffff;
--zyphr-border: #333333;
}
Custom Classes
<ZyphrInbox
classNames={{
container: 'my-inbox',
trigger: 'my-trigger',
dropdown: 'my-dropdown',
notification: 'my-notification',
unread: 'my-unread',
badge: 'my-badge',
empty: 'my-empty-state',
}}
/>
Tailwind CSS
The component works great with Tailwind:
<ZyphrInbox
classNames={{
container: 'relative',
trigger: 'p-2 rounded-full hover:bg-gray-100',
dropdown: 'absolute right-0 mt-2 w-80 bg-white rounded-lg shadow-lg',
notification: 'px-4 py-3 hover:bg-gray-50 cursor-pointer',
unread: 'bg-blue-50',
}}
/>
TypeScript
Full TypeScript support:
import type {
Notification,
NotificationPreferences,
ZyphrInboxProps,
} from '@zyphr/inbox-react';
const handleClick = (notification: Notification) => {
console.log(notification.id, notification.title);
};
Server-Side Rendering
The component handles SSR gracefully:
// Next.js App Router
'use client';
import { ZyphrProvider, ZyphrInbox } from '@zyphr/inbox-react';
export function Inbox({ userId }: { userId: string }) {
return (
<ZyphrProvider publicKey="za_pub_xxx" subscriberId={userId}>
<ZyphrInbox />
</ZyphrProvider>
);
}
Requirements
- React 18.0 or later
- Modern browser (Chrome, Firefox, Safari, Edge)