Sign in

Inbox

Create an inbox with Liveblocks. Show new activity in realtime, receive comment and mention notifications automatically, trigger custom notifications from your back end, and deliver missed activity by email, Slack, Microsoft Teams, and more.

Inbox

Inbox inside the Next.js Starter Kit

Features

Get started

Choose the channels you need. Each guide uses Next.js and can be combined with the others.

Implementation

This is an overview of how each feature can be implemented. Unlike other Liveblocks products, Notifications is project-based rather than room-based—a user receives notifications from every room in one inbox, and only LiveblocksProvider is required, not RoomProvider. Combine it with Comments for automatic thread notifications, and with your back end for custom application events. In multi-tenant apps, users can have a separate inbox per workspace.

In-app inbox

List the current user’s notifications with useInboxNotifications and render them with the ready-made InboxNotification and InboxNotificationList components. New notifications appear in realtime, without polling, and each built-in kind is rendered for you.

import { useInboxNotifications } from "@liveblocks/react/suspense";import { InboxNotification, InboxNotificationList } from "@liveblocks/react-ui";
function Inbox() { const { inboxNotifications } = useInboxNotifications();
return ( <InboxNotificationList> {inboxNotifications.map((inboxNotification) => ( <InboxNotification key={inboxNotification.id} inboxNotification={inboxNotification} /> ))} </InboxNotificationList> );}

Learn more under default components.

Unread indicators

Display an unread notifications count badge on your inbox button with useUnreadInboxNotificationsCount, plus add useMarkAllInboxNotificationsAsRead and useDeleteAllInboxNotifications to mark notifications as read and delete them.

import {  useMarkAllInboxNotificationsAsRead,  useUnreadInboxNotificationsCount,  useDeleteAllInboxNotifications,} from "@liveblocks/react/suspense";
function InboxHeader() { const { count } = useUnreadInboxNotificationsCount(); const markAllAsRead = useMarkAllInboxNotificationsAsRead(); const deleteAll = useDeleteAllInboxNotifications();
return ( <header> <span>{count} unread</span> <button onClick={markAllAsRead}>✅ Mark all as read</button> <button onClick={deleteAll}>❌ Delete all</button> </header> );}

Learn more under hooks.

Collaboration notifications

Comments sends thread notifications automatically when users are mentioned or receive replies in threads they’re participating in. Read state stays in sync both ways—viewing a thread marks its notification as read, and the default Thread component shows unread indicators. When rendering a thread notification, fetch its thread data with useInboxNotificationThread.

import { useInboxNotificationThread } from "@liveblocks/react/suspense";
function ThreadPreview({ inboxNotificationId,}: { inboxNotificationId: string;}) { const thread = useInboxNotificationThread(inboxNotificationId);
return <span>{thread.comments.length} comments</span>;}

Learn more under notification kinds.

Custom notifications

Notify users about your own application events, such as a completed export, a shared document, or a completed AI task, with Liveblocks.triggerInboxNotification. Custom kinds start with a $, and activityData holds whatever your UI needs to render the notification.

import { Liveblocks } from "@liveblocks/node";
const liveblocks = new Liveblocks({ secret: process.env.LIVEBLOCKS_SECRET_KEY!,});
await liveblocks.triggerInboxNotification({ userId: "olivier@example.com", kind: "$fileUploaded", subjectId: "file-123", activityData: { fileName: "quarterly-report.pdf", uploadedBy: "Quinn", },});

Render each custom kind with InboxNotification.Custom by passing a kinds prop to the default component.

import { InboxNotification } from "@liveblocks/react-ui";
<InboxNotification inboxNotification={inboxNotification} kinds={{ $fileUploaded: (props) => ( <InboxNotification.Custom {...props} title="New file uploaded" aside="📁"> {props.inboxNotification.activities[0].data.fileName} </InboxNotification.Custom> ), }}/>;

Learn more under rendering notification kinds differently.

Notification batching

You can batch groups of custom notifications into a single notification, helping you to avoid flooding inboxes, which works similarly to how thread notifications group new comments from the same thread. Enable batching for a custom kind in the dashboard, then trigger notifications with the same subjectId—each new activity updates the existing notification.

const options = {  userId: "olivier@example.com",  kind: "$fileUploaded",  // Same subjectId, so activities are added to one notification  subjectId: "file-123",};
await liveblocks.triggerInboxNotification({ ...options, activityData: { status: "processing" },});
await liveblocks.triggerInboxNotification({ ...options, activityData: { status: "complete" },});

Learn more under notification batching.

Subscription settings

Each user can control which thread notifications they receive in each room: "all" for everything, "replies_and_mentions" by default, or "none" to mute the room. Build a mute control with useRoomSubscriptionSettings.

import { useRoomSubscriptionSettings } from "@liveblocks/react/suspense";
function MuteButton() { const [{ settings }, updateSettings] = useRoomSubscriptionSettings();
return ( <button onClick={() => updateSettings({ threads: "none" })}> {settings.threads === "none" ? "Muted" : "Mute this document"} </button> );}

Settings can also be changed server-side with Liveblocks.updateRoomSubscriptionSettings, for example to subscribe a document’s author to everything in their document.

Notification settings

Let users choose which notification kinds they receive on each delivery channel—email, slack, teams, and webPush—with useNotificationSettings. Each kind must first be enabled on your project’s notifications dashboard page.

import { useNotificationSettings } from "@liveblocks/react/suspense";
function NotificationSettings() { const [{ settings }, updateSettings] = useNotificationSettings();
return ( <label> Receive thread notifications by email: <input type="checkbox" checked={settings.email?.thread} onChange={(e) => updateSettings({ email: { thread: e.target.checked } }) } /> </label> );}

The notification settings guide details how to build a complete settings panel for your application.

Email and other channels

Liveblocks sends a notification webhook event when a user has unread notifications on an enabled channel, by default up to every 30 minutes per user. Handle it on your server to deliver email, Slack, Teams, or web push notifications. For comment emails, @liveblocks/emails fetches and styles the unread comments for you.

import { prepareThreadNotificationEmailAsReact } from "@liveblocks/emails";import {  isThreadNotificationEvent,  Liveblocks,  WebhookHandler,} from "@liveblocks/node";
const liveblocks = new Liveblocks({ secret: process.env.LIVEBLOCKS_SECRET_KEY!,});const webhookHandler = new WebhookHandler(process.env.WEBHOOK_SECRET!);
export async function POST(request: Request) { const event = webhookHandler.verifyRequest({ headers: request.headers, rawBody: await request.text(), });
if (isThreadNotificationEvent(event)) { const emailData = await prepareThreadNotificationEmailAsReact( liveblocks, event );
// Render `emailData` and send the email with your provider // ... }
return new Response(null, { status: 200 });}

For custom kinds, fetch the notification with Liveblocks.getInboxNotification and format the message yourself. Learn more under email notifications.

Workspace inboxes

If your app has multiple workspaces or tenants, use organizations to give each user a separate inbox per workspace. Notifications from rooms created with an organizationId belong to that organization automatically, and custom notifications can be assigned to one when triggered.

await liveblocks.triggerInboxNotification({  userId: "olivier@example.com",  kind: "$fileUploaded",  subjectId: "file-123",  activityData: { fileName: "quarterly-report.pdf" },  organizationId: "acme-corp",});

Authenticate each user with the organizationId of their current workspace—their token only grants access to that organization, so useInboxNotifications and unread counts only show that workspace’s notifications.

const { body, status } = await liveblocks.identifyUser({  userId: "olivier@example.com",  organizationId: "acme-corp",});

Learn more under Organizations.

Examples

Explore complete examples that combine the features described above.