App Shells & Panels

Notification Triage Panel

A notification center panel grouped by date or source, with unread/all tabs and collapsible groups; on a bulk clear the items sweep out in sequence and the group height springs closed behind them.

Bring this into your project

Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.

npx webberui@latest add notification-triage-panel.en

New project or installation trouble? Start here →

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

Active edited usageDefault props
<NotificationTriagePanel/>

The complete example includes the preview layout and data. Install the component first, then paste this file into your project.

Installation

npx webberui@latest add https://webberui.com/r/notification-triage-panel.en.json

Or, once registries are configured in components.json, install it as @webberui/notification-triage-panel.

Usage

import {
  NotificationTriagePanel,
  type TriageNotification,
} from "@/components/ui/notification-triage-panel";

const notifications: TriageNotification[] = [
  {
    id: "1",
    title: "Aria mentioned you",
    description: "Replied to your comment in the thread.",
    source: "Slack",
    accent: "violet",
    timestamp: Date.now() - 4 * 60_000,
  },
  {
    id: "2",
    title: "PR #482 is waiting for your review",
    source: "GitHub",
    accent: "green",
    read: true,
    timestamp: Date.now() - 42 * 60_000,
  },
];

export function Example() {
  const [items, setItems] = React.useState(notifications);

  return (
    <NotificationTriagePanel
      notifications={items}
      onNotificationsChange={setItems}
      groupBy="date"
    />
  );
}

Without notifications, use defaultNotifications to enter uncontrolled mode, where the component manages the cleared and read states itself.

Props

NotificationTriagePanel

PropTypeDefaultDescription
notificationsTriageNotification[]—Notification list in controlled mode; pair it with onNotificationsChange
defaultNotificationsTriageNotification[][]Initial notification list in uncontrolled mode
onNotificationsChange(next: TriageNotification[]) => void—Callback for clearing or marking as read
groupBy"date" | "source""date"What to group by
tab"unread" | "all"—Current tab in controlled mode
defaultTab"unread" | "all""unread"Initial tab in uncontrolled mode
onTabChange(tab: "unread" | "all") => void—Callback for switching tabs
titleReact.ReactNode"Notifications"Panel title
relativeTimebooleantrueShow relative time; when off, shows HH:MM
localestring—Locale for time formatting (BCP 47)
emptyStateReact.ReactNode—What to show when the list is empty

TriageNotification

FieldTypeDescription
idstringStable unique identifier; the animation matches before and after states by it
titleReact.ReactNodePrimary title
descriptionReact.ReactNodeSecondary description text
sourcestringSource name; the grouping key when groupBy="source"
iconReact.ReactNodeIcon on the left; a bell by default
timestampDate | number | stringEvent time; the grouping key when groupBy="date"
readbooleanWhether it has been read
accent"neutral" | "blue" | "green" | "amber" | "red" | "violet"Accent color of the icon's base

How the animation works

  • Sequenced sweep-out: on a bulk clear (a whole group, or "clear all") the items slide out to the right in display order with a stagger; only after they have left does the height spring closed, and the items below flow up into the gap.
  • Group collapse and backfill: once a whole group is empty, the group block itself springs its height closed and the groups below rise naturally to fill in. Clicking a group header collapses or expands it, with the content height using the same spring transition.
  • One at a time: clicking an item marks it as read (in the unread tab it fades out and collapses); on hover or focus a clear button appears on the right, and clearing a single item sweeps out the same way.
  • Tuning points: both the collapse spring and the sweep duration can be influenced through tokens such as var(--wb-duration-fast,200ms), which also drive the transition colors.

Accessibility

  • The panel is a role="region", the tab strip a role="tablist", and the tab buttons carry aria-selected and support ← → to switch.
  • Group header buttons carry aria-expanded and aria-controls, so collapsing works from the keyboard.
  • The body of each notification is a button (mark as read) and the clear button is separate with its own aria-label, avoiding nested interactive elements.
  • When the user has "reduce motion" enabled at the system level, the sweep offsets and springs are replaced by a brief fade in / fade out, with no effect on how it is operated.