Skip to content

Components

NotificationsMenu

The bell next to the avatar: an unread badge on the trigger, a dropdown of recent notifications behind it. Standalone, theme-aware inside a Header — and a good neighbor: opening it closes the UserMenu, and vice versa.

In the header

Drop it into the Header’s actions slot, right next to the UserMenu. The bell shows unreadCount as a badge (capped at 99+), the panel lists your NotificationItem rows, and everything closes on outside click, Escape, or an item click. The two menus never stack: opening one closes the other, so each stays its own control instead of taking the other’s place.

Open fullscreen
9:41
header.tsx
import { AppShell, Header, NotificationsMenu, NotificationItem, UserMenu } from "appshell-react";
import { GitPullRequest, MessageSquare, Rocket } from "lucide-react";

<Header
  behavior="sticky"
  logo={<span className="font-bold">Console</span>}
  actions={
    <>
      <NotificationsMenu unreadCount={2}>
        <NotificationItem
          icon={<Rocket />}
          title="Deploy finished"
          description="terra-web #482 is live on production."
          time="2m"
          unread
          href="/deploys/482"
        />
        <NotificationItem
          icon={<MessageSquare />}
          title="Mara mentioned you"
          time="1h"
          unread
          onClick={openThread}
        />
        <NotificationItem icon={<GitPullRequest />} title="PR #98 merged" time="1d" />
      </NotificationsMenu>
      <UserMenu username="Mara Kealoha" initials="MK">…</UserMenu>
    </>
  }
/>

API

PropTypeDefaultDescription
unreadCountnumber0Unread notifications shown as a badge on the bell (caps at 99+). 0 hides the badge.
triggerReactNode—Replace the default bell button content entirely.
openboolean—Controlled open state. Omit for uncontrolled.
onOpenChange(open: boolean) => void—Open-state change requests.
align"start" | "end""end"Horizontal alignment of the panel relative to the trigger.
titleReactNode"Notifications"Panel heading.
actionReactNode—Rendered at the right of the heading — a "Mark all read" button. Clicks here keep the menu open.
footerReactNode—Pinned row under the list — a "View all notifications" link.
emptyStateReactNode—Shown instead of the built-in empty state when there are no items.
childrenReactNode—The list — NotificationItem elements or anything else. Clicks on role="menuitem" elements close the menu.
classNamestring—Extra classes for the dropdown panel.
triggerClassNamestring—Extra classes for the default trigger button.
aria-labelstring—Accessible name override for the trigger. Defaults to "Notifications", or the "Notifications ({count} unread)" label when unreadCount is greater than 0.
PropTypeDefaultDescription
title*string—Main line.
descriptionstring—Secondary line — clamps to two lines.
iconReactNode—Leading visual — a 16px icon or a small Avatar.
timestring—Short timestamp — "2m", "yesterday".
unreadbooleanfalseMarks the row unread: bolder title plus a dot.
hrefstring—Renders the row as a link (through the LinkProvider component) instead of a button.
onClick() => void—Action handler.
classNamestring—Extra classes.