npm package: @notifyhub/react
Description: React component library for Notifyhub.
Author: Notifyhub
Latest version: 0.0.6

# @notifyhub/react

`@notifyhub/react` is the React integration for NotifyHub.

It is built on top of `@notifyhub/client` and provides:

- one provider for client lifecycle and state subscription
- hooks for inbox, preferences, realtime, and low-level client access
- inbox UI components
- a preference center component
- toast primitives and an imperative toast store

## Install

```sh
npm install @notifyhub/react @notifyhub/client react react-dom
```

Import the package styles once near the top of your app:

```tsx
import '@notifyhub/react/styles.css';
```

## Recommended Setup

```tsx
import {InboxPopover, NotifyHubProvider, Toaster} from '@notifyhub/react';
import '@notifyhub/react/styles.css';

export function App() {
  return (
    <NotifyHubProvider
      config={{
        baseUrl: 'https://notifyhub.example.com',
        getSessionToken: async () => {
          const response = await fetch('/api/notifyhub/session', {
            method: 'POST',
          });

          if (!response.ok) {
            return null;
          }

          return await response.json();
        },
      }}
    >
      <InboxPopover />
      <Toaster />
    </NotifyHubProvider>
  );
}
```

`NotifyHubProvider` automatically connects the underlying client on mount and disconnects it on unmount.

## Using A Prebuilt Client Instance

If you want to manage the client yourself, pass a `client` instance instead of `config`.

```tsx
import {createNotifyHubClient} from '@notifyhub/client';
import {InboxPage, NotifyHubProvider} from '@notifyhub/react';

const notifyHubClient = createNotifyHubClient({
  baseUrl: 'https://notifyhub.example.com',
  getSessionToken: async () => {
    const response = await fetch('/api/notifyhub/session', {
      method: 'POST',
    });

    if (!response.ok) {
      return null;
    }

    return await response.json();
  },
});

export function NotificationsRoute() {
  return (
    <NotifyHubProvider client={notifyHubClient}>
      <InboxPage />
    </NotifyHubProvider>
  );
}
```

## Public API

### Providers

### `NotifyHubProvider`

Provides the active `NotifyHubClient` instance and subscribes React components to its snapshot state.

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `children` | `ReactNode` | yes | Provider children |
| `client` | `NotifyHubClient` | no | Existing client instance |
| `config` | `NotifyHubConfig` | no | Config used to create a client internally |
| `toastPosition` | `ToastPosition` | no | Toast container position, default `top-right` |
| `maxToasts` | `number` | no | Maximum number of simultaneous toasts, default `5` |
| `defaultToastDuration` | `number` | no | Default toast duration in milliseconds, default `5000` |

You must provide either `client` or `config`.

### `ToastProvider`

Low-level toast provider. `NotifyHubProvider` already includes one.

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `children` | `ReactNode` | yes | Provider children |
| `position` | `ToastPosition` | no | Toast position |
| `maxToasts` | `number` | no | Maximum visible toasts |
| `defaultDuration` | `number` | no | Default toast duration |
| `gap` | `number` | no | Gap between toasts |
| `offset` | `{x?: number; y?: number}` | no | Screen offset |

### Hooks

### `useNotifyHubClient()`

Returns the active `NotifyHubClient` instance from context.

### `useNotifyHub()`

Returns the raw `NotifyHubContextValue`:

- `client`
- `snapshot`
- `realtimeStatus`

Use this for advanced integrations. Most app code should prefer `useInbox()`, `usePreferences()`, or `useRealtimeStatus()`.

### `useInbox()`

Primary inbox hook for inbox state and actions.

Returned fields:

- `notifications`
- `unreadNotifications`
- `archivedNotifications`
- `unreadCount`
- `archivedCount`
- `hasUnread`
- `isLoading`
- `realtimeStatus`
- `lastError`
- `refresh()`
- `markAsRead(notificationId)`
- `markAsUnread(notificationId)`
- `markAllAsRead()`
- `archive(notificationId)`
- `unarchive(notificationId)`
- `remove(notificationId)`
- `getNotificationById(id)`
- `groupByDate(notifications)`
- `filterNotifications(notifications, filter)`

### `usePreferences()`

Loads preferences and topics, then returns:

- `preferences`
- `topics`
- `loading`
- `error`
- `refetch()`
- `updateTopicPreference(topicId, updates)`
- `subscribeToTopic(topicId)`
- `unsubscribeFromTopic(topicId)`
- `setGlobalMute(muted)`
- `setTopicChannelPreference(topicId, channel, enabled)`
- `setGlobalChannelPreference(channel, enabled)`

### `useRealtimeStatus()`

Returns:

- `status`
- `lastError`
- `isConnected`

### `useToast()`

Returns:

- `toast`

`toast` is the same imperative store exposed as `toastStore`.

### `useToastContext()`

Returns the low-level toast store instance.

### Components

### Inbox Components

#### `Inbox`

Renders `InboxPage` with the same props.

#### `InboxPage`

Full-page inbox view.

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `className` | `string` | no | Extra wrapper class |
| `title` | `string` | no | Page heading, default `Inbox` |
| `showSearch` | `boolean` | no | Show text search |
| `showTypeFilter` | `boolean` | no | Show type filter |
| `showDateGrouping` | `boolean` | no | Group results by date bucket |
| `emptyState` | `ReactNode` | no | Custom empty state |
| `onNotificationClick` | `(notification) => void` | no | Called after item click |
| `onActionClick` | `(notification, actionId) => void` | no | Called when a notification action button is clicked |

#### `InboxDropdown`

Popover-style inbox trigger and panel.

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `className` | `string` | no | Extra wrapper class |
| `position` | `'bottom-left' \| 'bottom-right'` | no | Panel alignment |
| `maxHeight` | `number` | no | Scroll container height |
| `bellIcon` | `ReactNode` | no | Custom trigger icon |
| `emptyState` | `ReactNode` | no | Custom empty state |
| `onNotificationClick` | `(notification) => void` | no | Called when a notification is opened |

#### `InboxPopover`

Renders `InboxDropdown` with the same props.

#### `InboxBadge`

Unread count badge.

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `className` | `string` | no | Extra class name |
| `max` | `number` | no | Cap display at `max+`, default `99` |

#### `NotificationItem`

Low-level notification row for custom inbox UIs.

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `notification` | `Notification` | yes | Notification to render |
| `onClick` | `(notification) => void` | no | Row click handler |
| `onArchive` | `(notification, event) => void` | no | Archive handler |
| `onUnarchive` | `(notification, event) => void` | no | Unarchive handler |
| `onToggleRead` | `(notification, event) => void` | no | Read/unread toggle handler |
| `onDelete` | `(notification, event) => void` | no | Delete handler |
| `onActionClick` | `(notification, actionId) => void` | no | Notification action handler |
| `showActions` | `boolean` | no | Shows row action controls |

### Preference Components

#### `PreferenceCenter`

Opinionated preference-management UI.

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `showFrequencyOptions` | `boolean` | no | Show frequency selectors |
| `showChannelOptions` | `boolean` | no | Show channel toggles |
| `allowGlobalMute` | `boolean` | no | Show global mute control |
| `headerText` | `string` | no | Header title |
| `onError` | `(error: Error) => void` | no | Error callback for failed mutations |
| `className` | `string` | no | Extra wrapper class |
| `theme` | `'light' \| 'dark' \| 'auto'` | no | Visual theme |

### Toast Components

#### `Toaster`

Listens for new inbox notifications and shows toast notifications automatically.

#### `ToastContainer`

Low-level toast portal renderer.

#### `Toast`

Low-level toast UI primitive. Most app code should use `Toaster` or `toastStore`.

### Toast Store API

The package exports an imperative `toastStore` for app-level toasts unrelated to inbox polling.

Available methods:

- `show(toast)`
- `info(message, options?)`
- `success(message, options?)`
- `warning(message, options?)`
- `error(message, options?)`
- `custom(component, options?)`
- `promise(promise, options)`
- `update(id, updates)`
- `dismiss(id)`
- `dismissAll()`
- `setPosition(position)`
- `setConfiguration(config)`
- `pauseTimer(id)`
- `resumeTimer(id)`

Example:

```tsx
import {toastStore} from '@notifyhub/react';

toastStore.success('Preferences saved');
```

### Icons

The package also exports these icon components:

- `ArchiveIcon`
- `BellIcon`
- `CheckIcon`
- `CloseIcon`
- `ErrorIcon`
- `InfoIcon`
- `SuccessIcon`
- `TrashIcon`
- `WarningIcon`

### Exported Types

### React-Specific Types

- `NotifyHubConfig`
- `NotifyHubContextValue`
- `InboxFilter`
- `DateGroup`
- `GroupedNotifications`
- `ToastPosition`
- `ToastOptions`
- `PromiseToastOptions`
- `ToastType`
- `ToastContextValue`
- `CustomToastProps`
- `PreferenceCenterProps`
- `UsePreferencesReturn`

### Re-Exported Client Types

- `Notification`
- `NotificationAction`
- `NotificationType`
- `Channel`
- `ChannelPreferences`
- `Frequency`
- `Topic`
- `TopicPreference`
- `RecipientPreferences`
- `GetTopicsResponse`

## Recommended Usage Patterns

- Use `NotifyHubProvider` to connect the package to your app.
- Use `useNotifyHubClient()` for low-level direct client access.
- Use `Toaster` when you want incoming notifications to automatically display as toasts.
- Use `toastStore` when you want manual, app-driven toast messages.

## Development

From the monorepo root:

```bash
bun install
```

- Playground:

```bash
cd packages/react && bun run play
```

- Playground typecheck:

```bash
cd packages/react && bun run play:check-types
```

- Tests:

```bash
cd packages/react && bun run test
```

- Build:

```bash
cd packages/react && bun run build
```

- Typecheck:

```bash
cd packages/react && bun run check-types
```