Notification center
A home for updates with read state, grouped information, and animated disclosure.
Preview
Notification center preview
Live · keyboard ready
When to use
- A bell in the app header with an unread badge and an inbox of recent updates.
- Notifications people can mark read, expand, and dismiss in bulk.
When not to use
- Use toast-stack for transient messages that should disappear.
- Use inbox-triage for a full-page inbox.
- Use badge for a count without a panel.
shadcn CLI
Also installs (2)
- manicat-foundation
- avatar
Manual install(+)
registry.jsonjson
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "notification-center",
"type": "registry:block",
"title": "Notification center",
"description": "A home for updates with read state, grouped information, and animated disclosure.",
"categories": [
"blocks"
],
"dependencies": [
"@radix-ui/react-popover",
"lucide-react",
"motion"
],
"registryDependencies": [
"https://manicat.viraza.net/r/manicat-foundation.json",
"https://manicat.viraza.net/r/avatar.json"
],
"files": [
{
"path": "registry/components/notification-center/notification-center.tsx",
"type": "registry:component",
"target": "@components/manicat/notification-center/notification-center.tsx"
},
{
"path": "registry/components/notification-center/notification-center.module.css",
"type": "registry:component",
"target": "@components/manicat/notification-center/notification-center.module.css"
},
{
"path": "registry/components/avatar/avatar.tsx",
"type": "registry:component",
"target": "@components/manicat/avatar/avatar.tsx"
},
{
"path": "registry/components/avatar/avatar.module.css",
"type": "registry:component",
"target": "@components/manicat/avatar/avatar.module.css"
}
],
"docs": "Docs and live preview: https://uiarc.dev/components/blocks/notification-center",
"meta": {
"tier": "free",
"kind": "block",
"docs": "https://manicat.viraza.net/components/blocks/notification-center",
"markdown": "https://uiarc.dev/components/blocks/notification-center/markdown"
}
}Usage
Install the component, then copy this into your app. Change the content and the props to fit your screen.
example.tsxtsx
"use client";
import { NotificationCenter } from "@/components/manicat/notification-center/notification-center";
import type { NotificationItem } from "@/components/manicat/notification-center/notification-center";
const notifications: NotificationItem[] = [
{ id: "n1", title: "Elia commented on Accordion", description: "The panel inner should keep its radius.", time: "2m", actor: { name: "Elia Kuratli", photo: "" } },
{ id: "n2", title: "Deploy succeeded", description: "manicat-docs · production", time: "18m", tone: "success" },
{ id: "n3", title: "Usage over 90%", description: "instance i-4332 is near capacity.", time: "1h", tone: "warning" },
];
export function NotificationCenterExample() {
return <NotificationCenter notifications={notifications} />;
}
Examples
Real screens rather than isolated props. Switch to Code to copy a whole example into your project.
Notification inbox A bell with an unread count, grouped by time, with tones for success and warnings.
Notification inbox
A bell with an unread count, grouped by time, with tones for success and warnings. preview
Live · keyboard ready
notification-inbox-a-bell-with-an-unread-count-grouped-by-time-with-tones-for-success-and-warnings-.tsxtsx
"use client";
import { NotificationCenter } from "@/components/manicat/notification-center/notification-center";
import type { NotificationItem } from "@/components/manicat/notification-center/notification-center";
const inbox: NotificationItem[] = [
{ id: "n1", title: "Elia commented on Accordion", description: "The panel inner should keep its radius.", time: "2m", actor: { name: "Elia Kuratli", photo: "" } },
{ id: "n2", title: "Deploy succeeded", description: "docs · production", time: "18m", tone: "success" },
{ id: "n3", title: "Usage over 90%", description: "Instance i-4332 is near capacity.", time: "1h", tone: "warning" },
];
export function NotificationCenterInboxExample() {
return <NotificationCenter notifications={inbox} />;
}
Keyboard interactions
| Keys | Action |
|---|---|
| Enter / Space | Opens the panel from the trigger, toggles a row's details, and activates actions. |
| Escape | Closes the panel and returns focus to the trigger (Radix Popover). |
| Tab | Moves through view toggles, rows, and row actions. |
Accessibility
- The trigger's aria-label includes the unread count; the visual badge is aria-hidden.
- The summary line is aria-live polite, and outgoing text copies are aria-hidden while they fade.
- Row toggles use aria-expanded and announce unread state; view toggles use aria-pressed.
- Focus moves to the next row, or a view toggle, after an item is read, dismissed, or cleared.
Motion
- The bell tilts when open; counts roll like an odometer in the direction of change.
- Rows open and collapse their own height on a spring, and bulk actions cascade down the list in under a quarter second.
- Reduced motion replaces height, roll, and tilt with instant fades.
Responsive behavior
- The panel is min(424px, 100vw minus 24px) wide and at most min(590px, 100vh minus 24px) tall, with the list scrolling inside.
- Below 380px padding tightens, the details indent shrinks, and the Mark all read label becomes icon only.
- Titles and previews ellipsize on one line.
Performance
- The list is not virtualized and keeps its own copy of the notifications; cap what you pass in.
- The panel mounts in a portal only while open.
Notes for AI
- Use in an app header for an inbox of updates. Use toast or toast-stack for transient messages and inbox-triage for a full-page inbox.
- notifications seeds internal state; sync changes back to your server through onReadChange and onDismiss rather than re-passing the array.