Inbox
Give every user a notification inbox they can come back to, with a bell, read state and archive.
Everything your app sends a user, by email, push or both, is kept for them as an inbox. One notification is one item however it was delivered, so an event sent both ways shows once, and the item says what each channel did.
Note
Requires @buildbase/sdk
0.0.71
or later, and a BuildBase server from
release 22
. Live updates need the same; on an older server the socket is
refused and the one-minute poll carries on.
Quick example
import { NotificationBell } from '@buildbase/sdk/react';
export function Header() {
return (
<header className="flex items-center justify-between">
<Logo />
<NotificationBell />
</header>
);
}import { NotificationInbox } from '@buildbase/sdk/react';
export default function NotificationsPage() {
return <NotificationInbox workspaceId="current" />;
}The bell shows the unread count and opens the inbox in a panel. The page shows the same list in full. Both read one shared list, so reading an item in either updates the other at once.
What goes in the inbox
Every notification sent to a user: the built-in events (invitations, billing,
credits, sign-in alerts), your own events sent with
bb.notification.send(), workflow emails and
pushes, and push campaigns.
Sign-in codes, magic links and password resets are never in it. A stored, readable login code is a credential; those stay in the console's email log only.
A user sees their own notifications and nobody else's. The whole workspace's history, every member's, is in the BuildBase console under Notifications → Delivery log, and on each workspace and user page.
Read, seen and archived
| State | Set when |
|---|---|
| Seen | The inbox was opened while the item was in it. Clears the bell's highlight. |
| Read | The item was opened in the inbox, or its email or push link was clicked. |
| Archived | The user archived it. It leaves the list and stays in their history. |
An email open never marks an item read: some mail clients load the tracking pixel before anyone reads the message. It is recorded on the item as "Opened", and a click is recorded as "Clicked".
When the user clicks an item, it is marked read and its link opens. A link to
another site goes through the same link check as a push click. A path inside
your app (/billing) opens as it is.
Scope
workspaceId chooses which items, on the hook and on both components:
| Value | Items |
|---|---|
| omitted | Everything the user has been sent |
'current' | The selected workspace's. Waits while none is selected |
| a workspace id | That workspace's |
'none' | Account notices that belong to no workspace (sign-in alerts) |
Your own UI
import { useNotifications } from '@buildbase/sdk/react';
export function NotificationList() {
const { notifications, counts, loading, markRead, markAllRead } =
useNotifications({ workspaceId: 'current' });
if (loading) return <Spinner />;
return (
<section>
<button onClick={markAllRead} disabled={counts.unread === 0}>
Mark all read ({counts.unread})
</button>
{notifications.map((item) => (
<button key={item.id} onClick={() => markRead(item.id)}>
{item.readAt ? item.title : <strong>{item.title}</strong>}
</button>
))}
</section>
);
}| Returned | Type | Description |
|---|---|---|
notifications | INotificationItem[] | Loaded items, newest first, archived ones left out |
unread | INotificationItem[] | The loaded items not yet read |
counts | { unread, unseen } | Server counts for the whole scope, not only loaded |
loading | boolean | True while the first page loads |
hasMore | boolean | Another page is available |
error | string | null | The last load or action error |
loadMore | () => Promise<void> | Append the next page |
markRead | (id) => Promise<void> | Read in the inbox |
markUnread | (id) => Promise<void> | Back to unread |
archive | (id) => Promise<void> | Out of the list, kept in history |
markAllRead | () => Promise<void> | Read everything in scope |
markSeen | () => Promise<void> | Clear the "new" highlight without reading anything |
refresh | () => Promise<void> | Fetch the first page again |
| Option | Type | Default | Description |
|---|---|---|---|
workspaceId | string | omitted | See Scope |
pageSize | number | 20 | Items per page, at most 100 |
pollIntervalMs | number | 60000 | How often to check for new items. 0 turns it off |
live | boolean | true | Hear about changes the moment they happen |
Edge cases
- Signed out: the list is empty and nothing loads.
- New items: arrive live, over one connection per tab shared by every bell and inbox on the page. The connection carries no content, only "your inbox changed", and the list refetches. The count is still polled every minute as a fallback, and straight away when the tab comes back into view.
- History window: the inbox shows as far back as your plan shows email history.
- Sending:
bb.notification.send()from the SDK is limited to 300 an hour per workspace, because each send lands in every recipient's inbox. - A failed action: the change is undone on screen and
erroris set.