Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Popover examples · Standard

Notifications Popover for React

A bell button with an unread count that opens a popover listing recent notifications.

Framework
UI library

A bell with an unread count that opens the latest notifications: a header with Mark all as read, a separator, and three entries with their times. Show recent activity in place, without leaving the page. In React, Base UI handles opening, dismissing and focus.

Preview

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.

FAQ

What does each notification row show?

notifications has three entries. Each li has a font-medium title and a text-muted-foreground text-xs time: New comment, New follower, and Update available. The list is a ul with flex flex-col gap-2.

How does the unread badge get its number?

The Badge renders notifications.length, which is 3. It uses pill, size="sm", and className="absolute -end-1 -top-1". The button aria-label is Notifications, then that count, then unread.

How does the bell become the trigger?

The trigger is a Button with variant="ghost" and size="icon-md". BellIcon has aria-hidden="true" and className="size-5". The badge is the other child of that button. Activating the button opens the panel. Pass the button to render on PopoverTrigger, and use PopoverContent as the panel.

What is in the panel header?

A justify-between row holds PopoverTitle reading Notifications and a ghost Button with size="sm" reading Mark all as read. A Separator sits under that row. PopoverContent is w-80 max-w-full with gap-3. This demo gives the mark-read button no handler yet.

Do the notifications update?

The array is a constant: three titles and the times 2 minutes ago, 1 hour ago, and 3 hours ago. The badge count is that array's length. Mark all as read has no handler in this file.

How does this panel dismiss?

Escape closes the panel and returns focus to the trigger. A click outside closes it too. The panel sits beside the button, and the rest of the page stays usable.