Skip to content

Notification Center

Collect notifications behind a bell, with unread counts, filters and actions.

import { NotificationCenter, NotificationItem } from '@youj/react'

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use a notification center for things that happened while someone was away and that they'll act on later: mentions, invites, deploys, alerts. Press the bell to open it, or simulate an arrival and watch the bell swing and the count roll. For a message about something that just happened in front of the person, use a Toast. For a history of events on one object, use a Timeline.

import { NotificationCenter, type NotificationItem } from '@youj/react'

export function Inbox({ items }: { items: NotificationItem[] }) {
  return (
    <NotificationCenter
      notifications={items}
      onMarkRead={(id) => markRead(id)}
      onMarkAllRead={() => markAllRead()}
    />
  )
}

The center reads the list you pass and never changes it: marking read calls onMarkRead or onMarkAllRead, and you pass back the updated list. Each item has an id, a title and a time, and optionally:

  • actor, the person who did it (their avatar shows beside it), or an icon for system events.
  • body, a second line, clamped to two lines.
  • read. Unread items carry a dot and count towards the badge.
  • mention, which lists the item under Mentions.
  • actions, buttons under the item, such as Reply or Accept.
  • summary, plain text for announcements and toasts when title isn't a string.

It opens in a Popover aligned to the bell's end on desktop, and in a bottom Sheet below the md breakpoint.

Arrivals and toasts

A notification whose id wasn't in the previous list is an arrival. If it's unread, the bell swings once and it's announced. With the panel open, it takes its slot at the top of the list and the rest glide down.

useNotificationToasts also shows a Toast for each arrival. Pass enabled: !open so arrivals toast only while the panel is closed, and render one <Toaster /> in the app, as the Demo story does.

import { useState } from 'react'
const [open, setOpen] = useState(false)
useNotificationToasts(items, { enabled: !open })

<NotificationCenter notifications={items} open={open} onOpenChange={setOpen} />

Reference

Propsnotifications, onMarkRead, onMarkAllRead… (8)

Props

NotificationCenter props
PropType
notificationsRequiredreadonly NotificationItem[]–
onMarkRead(id: string) => void–
onMarkAllRead() => void–
openboolean–
onOpenChange(open: boolean) => void–
localestring

Default: the YoujProvider locale.

–
nowDate | number

The moment Today and relative times count from, pinned (a demo's clock, a test). Default: the clock when the list opens.

–
classNamestring–

useNotificationToasts(items, { enabled }) takes the same list, and toasts every arrival while enabled (default true).

MotionBell swing, count, badge… · reduced motion

Motion

Motion tokens
MomentTokens
Bell swingThe Youj BellIcon swings once per arrival; a second arrival mid-swing extends it
duration.deliberate

560ms

560ms
Countthe number rolls to its new value (Counter, roll)
duration.slowease.standard

340ms · cubic-bezier(0.2, 0, 0, 1)

340mscubic-bezier(0.2, 0, 0, 1)
Badgepops in from scale 0 on the first unread
duration.baseease.enter

220ms · cubic-bezier(0.16, 1, 0.3, 1)

220mscubic-bezier(0.16, 1, 0.3, 1)
Arrivalrises into its slot at the top and fades in (PresenceList)
duration.baseease.enter

220ms · cubic-bezier(0.16, 1, 0.3, 1)

220mscubic-bezier(0.16, 1, 0.3, 1)
Removalfades out, as when an item leaves the Unread tab
duration.fastease.exit

140ms · cubic-bezier(0.4, 0, 1, 1)

140mscubic-bezier(0.4, 0, 1, 1)
Reflowthe rest of the list glides to its new place
spring.smooth

0.36s, bounce 0

0.36s, bounce 0
Unread dotfades and shrinks to half when read
duration.baseease.standard

220ms · cubic-bezier(0.2, 0, 0, 1)

220mscubic-bezier(0.2, 0, 0, 1)
Tab indicatorglides to the chosen filter (SlidingIndicator)
spring.smooth

0.36s, bounce 0

0.36s, bounce 0

Reduced motion

The bell doesn't swing and the badge doesn't pop; the count and the list change in place.

Reduced motion

The bell stays still, the badge appears without the pop and new or removed items change the list in place. Arrivals are still announced, and toasts still show.

AccessibilityKeyboard, focus, announcements

Accessibility

  • The bell is a button whose name carries the count: "Notifications, 3 unread".
  • Arrivals are announced politely: "New notification: Mira Chen mentioned you in Q3 roadmap".
  • Unread items start with "Unread:" for screen readers, so the dot isn't the only signal.
  • Mark as read, Mark all as read and every action are real buttons. Mark as read shows on hover and focus, and always on touch screens.
  • Today and Earlier are labeled sections with their own headings, and the filters are Tabs with arrow-key navigation.
  • Times are <time> elements with a machine-readable dateTime.