Per-subject notification inbox for Convex with fan-out delivery, read/unread state, reactive queries, and sandboxed tables requiring no auth coupling.
npm install @vllnt/convex-notifications@vllnt/convex-notifications stores a notification inbox for each opaque subject reference. The application defines notification types and a typed payload, so the same component can serve different product surfaces.
deliver writes one notification per recipient, including fan-out to several recipients. list and unreadCount support inbox screens and badges through host queries. markRead and bounded markAllRead clear unread state. Retention cleanup removes eligible read notifications.
The host authenticates recipients, gates access, and chooses which events create notifications. This is an in-app data store; it does not send APNs/FCM push notifications, SMS, or email. Device delivery and channel preferences belong in your integration.
Maintained by [bntvllnt](https://bntvllnt.com) at [VLLNT](https://vllnt.com). Find the author on [GitHub](https://github.com/bntvllnt) and [X](https://x.com/bntvllnt).
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.
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.
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.
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.
@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.
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.
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.
@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.
@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.