Notification center
ProEverything that happened while you were away, behind a bell. The count is unread notices only. Opening one marks it read; "Mark all read" clears the rest. Each notice names the one thing to do next; one whose action has an href is a link to it, so it can open in a new tab too.
Preview
Install
With the Candor CLI
First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/notification-center
Brings in: locale, popover, relative-time, team, utils · npm: lucide-react@^1.67.0
Usage
"use client"
import * as React from "react"
import { NotificationCenter } from "@/components/notification-center"
import type { Notice } from "@/lib/team"
const minutesAgo = (m: number) => new Date(Date.now() - m * 60_000).toISOString()
const initial: Notice[] = [
{ id: "n1", kind: "approval-waiting", title: "Dev agent wants to run a migration on Production", detail: "Add region to orders", at: minutesAgo(3), read: false, action: { label: "Review" } },
{ id: "n2", kind: "guardrail-stopped", title: "A run was stopped at its budget", detail: "Backfill customers.region used its 30 minutes", at: minutesAgo(41), read: false, action: { label: "See the run" } },
{ id: "n3", kind: "run-finished", title: "Index on orders.customer_id is live on Staging", at: minutesAgo(180), read: true },
{ id: "n4", kind: "invite", title: "Priya invited you to the Payments workspace", at: minutesAgo(60 * 26), read: true, action: { label: "Accept" } },
]
export default function Example() {
const [notices, setNotices] = React.useState(initial)
return (
<div className="flex justify-end">
<NotificationCenter
notices={notices}
onOpen={(id) => setNotices((ns) => ns.map((n) => (n.id === id ? { ...n, read: true } : n)))}
onMarkAllRead={() => setNotices((ns) => ns.map((n) => ({ ...n, read: true })))}
/>
</div>
)
}<NotificationCenter>
| Prop | Type | Default | Description |
|---|---|---|---|
| noticesrequired | Notice[] | — | The notices, unread first and then newest; only unread ones count on the bell. |
| onOpen | ((id: string) => void) | undefined | — | Called with a notice's id when it's opened; the app marks it read. A notice with `action.href` also follows its link. |
| onMarkAllRead | (() => void) | undefined | — | Shows "Mark all read" while there are unread notices; called when it's pressed. |
| className | string | undefined | — | Extra classes for the outer element. |
Strings
The fixed words, in English by default. Change any of them under notificationCenter in <LocaleProvider strings={{ … }}>; the table is exported as notificationCenterStrings.
| Key | English |
|---|---|
| notifications | Notifications |
| notificationsUnread | Notifications, {count} unread |
| markAllRead | Mark all read |
| allCaughtUp | You’re all caught up. |
| unread | Unread |