Skip to content

Components · Collaboration

NotificationCenter

Bell with an unread count and a panel: All and Unread tabs, day groups, mark read, load more and an empty state.

@rookcom/monarch-react/collaborationComponent

Bell and panel

Usage examples

Custom trigger

Purpose

Gives every app the same in-app inbox: a bell in the header or rail that shows how many notifications are unread and opens a panel to read, open and clear them. The product supplies the items and handles reading and navigation.

Use when

  • An app header or icon rail that needs mentions, assignments and replies.
  • Any surface that marks notifications read when opened.

Avoid when

  • A full-page notifications inbox with bulk actions: build it from ActivityTimeline or a list.
  • Transient confirmations: use Toast.
  • Per-type delivery settings: use SwitchSettingsList on a settings page and link to it in footer.

Design principles

  • Unread is never colour alone: bold text, a dot and a hidden “Unread:” prefix.
  • The count on the bell is in its accessible name (“Notifications, 3 unread”), the visual badge is hidden.
  • One panel, two tabs: All and Unread, through Monarch TabsFilter, rather than separate pages.
  • Reading is the product’s decision: markReadOnSelect is on by default, and both read actions are callbacks.

API reference

import { NotificationCenter } from '@rookcom/monarch-react/collaboration';
ExportKindRenderingRoot class
NotificationCentercomponentclient.mn-notification-center

The ref and button attributes go to the built-in bell. With trigger, pass your own element and give it the count in its name.

open / defaultOpen / onOpenChange and filter / defaultFilter / onFilterChange work controlled or uncontrolled.

Items are filtered client-side by read on the Unread tab; refetch in onFilterChange when you page on the server.

Generated from the TypeScript declarations of packages/react/src/components/collaboration/notification-center.

Properties

NotificationCenter

A bell with the unread count that opens a panel of notifications: All and Unread tabs, day groups, mark one or all as read, load more and an empty state. The product supplies the data and callbacks.

PropTypeDescription
items requiredreadonly NotificationItem[]Loaded notifications, newest first.
align"center" | "start" | "end"Panel alignment against the trigger. Default `end`.
childrenReactNode
classNamestring
defaultFilterNotificationFilter
defaultOpenboolean
disabledboolean
emptyReactNodeReplaces the built-in empty state.
errorReactNodeFirst-load failure, e.g. an InlineErrorRetry. Replaces the list.
filterNotificationFilterThe All / Unread tab. The list is filtered by `read`; fetch server-side too if you page.
footerReactNodeBelow the list: a link to all notifications or to preferences.
groupByDaybooleanGroup items under Today, Yesterday and dates. Default `true`.
hasMorebooleanAnother page exists. Shows LoadMore at the end of the list.
idstring
loadingbooleanFirst load: shows a skeleton.
loadingMoreboolean
loadMoreErrorboolean
localestring
markReadOnSelectbooleanCall `onMarkRead` when an unread item is activated. Default `true`.
messagesPartial<NotificationCenterMessages>
nowDateInputReference time for relative times and day groups.
onFilterChange((filter: NotificationFilter) => void)
onLoadMore(() => void)
onMarkAllRead(() => void)Marks everything read. Without it there is no "Mark all as read" button.
onMarkRead((id: string) => void)Marks one item read. Without it items have no "Mark as read" button.
onOpenChange((open: boolean) => void)
onSelect((item: NotificationItem) => void)An item was activated. Navigate here, or use `href`.
openboolean
panelClassNamestringClass for the panel.
side"top" | "right" | "bottom" | "left"
timeZonestring
triggerReactElement<unknown, string | import("/home/runner/work/monarch/monarch/node_modules/@types/react/index").JSXElementConstructor<any>>A custom trigger element (a rail item, a menu row). Give it the unread count in its name.
unreadCountnumberUnread total from the server, which may exceed the loaded items. Default: unread items in `items`.

Variants

PropValuesDescription
groupByDay'true''false'Today, Yesterday and dated headings, or one flat list.
trigger'bell (default)''custom element'A rail item or menu row instead of the bell button.

States

StateExposed asBehaviour
Unreaddata-unreadBold title, accent dot, “Mark as read” button when onMarkRead is set.
Read–Muted text, no dot.
Loading–Skeleton rows.
Error–The error node replaces the list.
Empty–“You’re all caught up”, or “No unread notifications” on the Unread tab.
Opendata-state="open"The bell stays highlighted while the panel is open.

Interaction behaviour

  • Clicking the bell opens the panel; clicking outside or Escape closes it and returns focus to the bell.
  • Activating an item calls onMarkRead (if unread and markReadOnSelect) and onSelect; with href it is a real link.
  • “Mark all as read” is disabled when nothing is unread.
  • LoadMore at the end of the list calls onLoadMore.

Keyboard support

KeysAction
Enter / SpaceOn the bell, opens the panel and moves focus into it.
TabMoves through Mark all as read, the tabs, each item and its Mark as read button.
Arrow Left / Right, Home / EndOn the tabs, switch between All and Unread.
EscapeCloses the panel and returns focus to the bell.

Accessibility

Role
button (bell) controlling a dialog (panel)
Accessible name
Bell: “Notifications, N unread”. Panel: labelled by its heading.
  • The bell has aria-haspopup="dialog" and aria-expanded.
  • Day headings label their lists, so a screen reader hears “Today, list, 2 items”.
  • Unread items start with hidden “Unread:” text; the dot is decorative.
  • The tabs are a real tablist with one panel; the panel is scrollable and focusable.

Responsive behaviour

  • The panel is 24rem wide, never wider than the viewport minus 1rem, and at most 34rem tall; the list scrolls inside it.
  • Day headings stick to the top while scrolling.

Theme support

  • The panel is a Monarch popover surface. The badge uses danger-solid with a ring in the page surface colour; the unread dot uses accent-solid.
  • Forced colours draw the badge and dot in CanvasText.

Do and don't

Do

  • Write titles as sentences with the actor first: “Ana Lopez mentioned you in Launch checklist”.
  • Pass unreadCount from the server when you page.
  • Link to notification settings in footer.

Don't

  • Do not show the badge count anywhere without the bell’s accessible name.
  • Do not mark items read just because the panel opened.
  • Do not put actions other than read state inside the list rows.

Dependencies

KindDepends on
npm packagesreact
Monarch componentsPopover, Tabs, Button, Avatar, RelativeTime, LoadMore, EmptyState, Skeleton

Testing coverage

  • Names the bell with the unread count; caps the badge at 99+; no badge when all are read.
  • Opens a labelled dialog, moves focus in, and Escape returns focus to the bell.
  • Groups by day and marks unread items for screen readers; href items are links.
  • Selecting marks unread items read and calls onSelect; Mark as read and Mark all as read.
  • All and Unread tabs by keyboard, uncontrolled and controlled; controlled open.
  • Empty states for both tabs; load more; loading and error.
  • Flat list, footer, custom trigger, translations, forwarded ref.
CheckCovered
Axe in jsdomYes
Server renderingYes
Browser suitesVisual baselines: /__gallery/notification-center (closed and open); Cross-browser axe: /components/notification-center

Tests: packages/react/src/components/collaboration/notification-center