NotificationsBlock
A notification panel with support for read/unread states, avatars, and timestamps.
Import
import { NotificationsBlock } from "@raydenui/ui/blocks";Usage
<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 | 6defaulting to3. It is now the string union"h1"–"h6"defaulting to"h2", so that a page owned by the block can reachh1at all. ReplaceheadingLevel={3}withheadingLevel="h3". Code that passed nothing now renders anh2where it previously rendered anh3, which removes a skipped heading level on a page whose only other content is the block.
Props
NotificationsBlockProps
| Prop | Type | Default | Description |
|---|---|---|---|
items | NotificationItem[] | — | Required. The notifications to show. |
title | string | "Notifications" | Block title. |
headingLevel | "h1"–"h6" | "h2" | Element for the block title. |
unreadCount | number | — | Supplied by your application. A badge appears when above zero. |
className | string | — | Additional class names. |
NotificationItem
| Prop | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique identifier. |
avatar | ReactNode | Yes | Avatar element. |
text | ReactNode | Yes | Notification message. |
date | string | Yes | Date label. |
time | string | Yes | Relative time, e.g. “10 mins”. |
unread | boolean | No | Adds the visible “Unread” text and dot. |
link | { label: string; href?: string; onClick?: () => void } | No | A destination or an action. |
badge | string | No | Short qualifier beside the metadata. |
content | file | comment | cta object | No | Attached 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-setsizeand 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.
unreadis supplied per item andunreadCountis supplied by your application; there is no mark-all-read control or callback. - The panel uses
bg-whitewith a dark-mode override rather than the semantic surface roles, so a consumer--color-surfaceoverride does not reach it.