# Notification Center

> An animated notification center for React and shadcn, grouped by day, with mark all read.

An inbox for product events, grouped by day. New notifications rise into Today while the rows below slide down and the count rolls, and Mark all read collapses every unread dot in turn.

- Tier: Eased Pro
- Interaction: Loading
- Page: https://eased.dev/components/notification-center
- Installs: motion

## Install

Notification Center ships with Eased Pro (https://eased.dev/#pricing). Members add the `@eased-pro` registry with their token (https://eased.dev/account), then run:

```bash
npx shadcn add @eased-pro/notification-center
```

An inbox for product events, grouped by day: new notifications rise into Today, and Mark all read collapses every unread dot in turn. Built on Group By List, Badge, Avatar and Button.

## When to use

- The bell menu or inbox page of a SaaS product: comments, shares, failed syncs, billing alerts.
- When notifications arrive live and the list must make room without jumping.

## When not to use

- A transient confirmation: use [Toast](/components/toast).
- An audit trail of who changed what: use a timeline or audit log.

## Usage

```tsx example.tsx
import { NotificationCenter } from "@/components/eased/notification-center/notification-center";

<NotificationCenter
  notifications={items}
  onNotificationsChange={setItems}
  onOpen={(n) => router.push(`/inbox/${n.id}`)}
/>
```

A notification is a timeline-core `TimelineEvent` with `data: { unread, context? }`. Rows with an `actor` show their avatar; the rest show an icon for their `kind` (comment, report, alert, billing, member) tinted by `tone`.

## API reference

### NotificationCenter

| Prop | Type | Default | Description |
|---|---|---|---|
| `notifications` / `defaultNotifications` / `onNotificationsChange` | `Notification[]` | `[]` | Newest first. Prepend to add one; it rises in. |
| `onOpen` | `(n) => void` | | A row was opened; it is marked read too. |
| `now` | `number` | mount time | For day groups and relative times. |
| `title` | `string` | "Notifications" | |
| `icons` | `Record<kind, ReactNode>` | | Override the icon per `kind`. |
| `locale` | `string` | `"en-US"` | |
| `className` | `string` | `""` | Give it a height; the list scrolls with fades. |

## Keyboard

| Key | Action |
|---|---|
| `Tab` | Moves to Mark all read, then through the rows. |
| `Enter` / `Space` | Opens a row and marks it read. |

## Accessibility

- Day groups are headings; each row is a button whose text includes its actor, title, context and time.
- The unread count badge is announced as "3 unread"; unread rows say "Unread".
- Mark all read stays focusable when there is nothing to mark, announced as disabled.

## Motion

A new notification rises 10px on the snappy spring while the rows under it slide down on `smooth` and the count rolls. Mark all read shrinks each unread dot top to bottom, 35ms apart (the cascade stays under 320ms). Rows never resize. Reduced motion: rows and dots fade only.

## Notes for AI

Use it inside a Popover for a bell menu or full height on an inbox page. Keep the list controlled and prepend new items; keep ids stable.

Don't sort the list yourself into groups or render your own headers: grouping is by day from `now`. Don't remove read items to show they're read.

## Related

- [Group By List](/components/group-by-list): the grouped list underneath.
- [Badge](/components/badge): the rolling count.
