Notifications Inbox

Per-subject notification inbox for Convex with fan-out delivery, read/unread state, reactive queries, and sandboxed tables requiring no auth coupling.

Installation

npm install @vllnt/convex-notifications

About Notifications Inbox

Notifications Inbox is a per-subject directed inbox for Convex: `deliver` fans out one notification per recipient, every notification arrives unread, and `list`/`unreadCount`/`get` are reactive queries that stream changes to connected clients without extra subscriptions. Server-sourced timestamps prevent caller-supplied times, a daily cron purges read notifications past retention in bounded batches (unread are never purged), and the typed `Notifications<TPayload>` client with `payloadValidator` narrows the stored payload at the boundary. Notification data lives in the component's own sandboxed tables — no blast radius into your schema — the host owns auth and resolves identity, and the component runs identically on Convex Cloud and self-hosted convex-backend.

Benefits

Use cases

how to build a notification inbox in Convex with unread count

The @vllnt/convex-notifications component provides inbox.list() and inbox.unreadCount() methods that work as reactive Convex queries. You re-export them from your host app and call them with useQuery in React. Both are scoped to a subjectRef so each user's inbox is isolated.

fan-out notifications to multiple users in Convex

inbox.deliver(ctx, recipients, type, payload?) accepts a single subjectRef or an array and writes one notification row per recipient in a single mutation. It returns a notificationIds array so you can track each delivery individually.

mark all notifications as read Convex mutation

The @vllnt/convex-notifications component exposes inbox.markRead(ctx, notificationId) for individual notifications and inbox.markAllRead(ctx, subjectRef) for bulk marking. markAllRead is bounded and self-rescheduling so it handles large inboxes without hitting Convex mutation limits.

how to add a daily cleanup cron for old notifications in Convex

The component ships with a built-in bounded purge via inbox.purge() and registers a daily cron that sweeps read notifications past the retention window in batches. Unread notifications are never purged automatically.

Frequently asked questions

Does @vllnt/convex-notifications handle authentication or user identity?

@vllnt/convex-notifications is auth-agnostic. The host application resolves identity and decides which subjectRef a caller may read from or deliver to. The component accepts any opaque string as a subjectRef and never inspects or stores user session data directly.

How does the component prevent cross-user inbox leakage?

Every list and unreadCount call in @vllnt/convex-notifications is keyed to a single subjectRef and cannot span another subject's inbox. Tables are sandboxed and reachable only through the component's exported functions, so host or sibling tables are never touched.

Can I store custom data with each notification?

Yes. The Notifications class is generic: new Notifications<TPayload>(components.notifications, { payloadValidator }) lets you type and validate the payload at the component boundary using a Convex validator. The stored payload stays opaque to the component itself and is returned as-is in NotificationView results.

Is this component safe to mount multiple times in the same Convex app?

@vllnt/convex-notifications is mount-safe. Each named app.use mount in convex.config.ts creates an isolated sandbox, so multiple instances do not share tables or interfere with each other.

Does the component include any React UI or frontend code?

@vllnt/convex-notifications is backend-only and has no ./react entry point. Inbox lists, unread counts, and individual notifications are fetched through standard useQuery calls against query functions you re-export from your host app using the component's list, unreadCount, and get methods.

Links