Skip to Content
BlocksNotificationsBlock

NotificationsBlock

A notification panel with support for read/unread states, avatars, and timestamps.

Import

import { NotificationsBlock } from "@raydenui/ui/blocks";

Usage

Preview
<NotificationsBlock unreadCount={7} items={[ { id: "1", avatar: <Avatar type="image" src="..." size="sm" />, text: ( <> <span className="font-medium">David</span> left a comment on{" "} <span className="font-medium text-primary-400">Site redesign</span> </> ), date: "Mar 21, 2024", time: "10 mins", unread: true, }, { id: "2", avatar: <Avatar type="image" src="..." size="sm" />, text: ( <> <span className="font-medium">Sarah</span> shared a file with you </> ), date: "Mar 20, 2024", time: "2 hours", unread: false, }, ]} />

With File Attachment

<NotificationsBlock items={[ { id: "1", avatar: <Avatar type="image" src="..." size="sm" />, text: "David shared a file", date: "Mar 21, 2024", time: "10 mins", unread: true, content: { type: "file", title: "Meeting minutes", size: "13MB", fileType: "PDF File", date: "11 Sept 23", }, }, ]} />

With Action Buttons

<NotificationsBlock items={[ { id: "1", avatar: <Avatar type="image" src="..." size="sm" />, text: "You have a pending invitation", date: "Mar 21, 2024", time: "10 mins", content: { type: "cta", primaryAction: { label: "Accept" }, secondaryAction: { label: "Decline" }, }, }, ]} />

Heading level

headingLevel is the element name, so the block fits any page outline. A standalone notifications page can own the h1; a dashboard panel beside other panels stays at h2.

<NotificationsBlock headingLevel="h1" title="Notifications" items={items} />

Breaking change. This prop was a numeric union 2 | 3 | 4 | 5 | 6 defaulting to 3. It is now the string union "h1"–"h6" defaulting to "h2", so that a page owned by the block can reach h1 at all. Replace headingLevel={3} with headingLevel="h3". Code that passed nothing now renders an h2 where it previously rendered an h3, which removes a skipped heading level on a page whose only other content is the block.

Props

NotificationsBlockProps

PropTypeDefaultDescription
itemsNotificationItem[]—Required. The notifications to show.
titlestring"Notifications"Block title.
headingLevel"h1"–"h6""h2"Element for the block title.
unreadCountnumber—Supplied by your application. A badge appears when above zero.
classNamestring—Additional class names.

NotificationItem

PropTypeRequiredDescription
idstringYesUnique identifier.
avatarReactNodeYesAvatar element.
textReactNodeYesNotification message.
datestringYesDate label.
timestringYesRelative time, e.g. “10 mins”.
unreadbooleanNoAdds the visible “Unread” text and dot.
link{ label: string; href?: string; onClick?: () => void }NoA destination or an action.
badgestringNoShort qualifier beside the metadata.
contentfile | comment | cta objectNoAttached content. See below.

content is a discriminated union on type:

  • { type: "file", title, size?, fileType?, date? }
  • { type: "comment", avatar, author, timestamp, comment, reactions?, replies? }
  • { type: "cta", primaryAction: { label, onClick? }, secondaryAction?: { label, onClick? } }

States

  • default — the notification list.
  • unread — per item, through item.unread.

There is no empty, loading or error state. Render your own around the block.

Accessibility

  • A named section, labelled by the block title.
  • Native list semantics with an explicit role="list", so the list stays exposed when list markers are removed. This is deliberately not an ARIA feed: the feed pattern also needs per-article accessible names, aria-posinset, aria-setsize and a focus-driven loading model this block does not implement.
  • Unread state is carried by visible “Unread” text as well as a dot, so it does not rest on colour.
  • Each notification is an article, and metadata wraps according to the width the item actually has.

Known limitations

  • The block marks nothing read. unread is supplied per item and unreadCount is supplied by your application; there is no mark-all-read control or callback.
  • The panel uses bg-white with a dark-mode override rather than the semantic surface roles, so a consumer --color-surface override does not reach it.
Last updated on