Notifications Block
Notifications is a notification panel built on MUI.
Installationโ
- npm
- yarn
- pnpm
- bun
npm install @nodeblocks/frontend-notifications-block
yarn add @nodeblocks/frontend-notifications-block
pnpm add @nodeblocks/frontend-notifications-block
bun add @nodeblocks/frontend-notifications-block
What You Needโ
| Item | Why it matters |
|---|---|
isNotificationsOpen | Controls whether the popover panel is open |
setIsNotificationsOpen | Lets the panel close (for example, on click outside) |
notificationsList | Array of notification items to render in the panel |
onClick | Handles bell button clicks (typically toggles open state) |
unreadCount (optional) | Drives the badge count on the bell button |
heading (optional) | Panel heading above the list |
emptyStateMessage (optional) | Copy shown when the list is empty |
Notifications does not own panel open state. Keep isNotificationsOpen in your app and pass setIsNotificationsOpen so the popover can close correctly.
Each item in notificationsList supports:
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Notification heading |
createdAt | string | Yes | ISO date string used for relative time display |
text | ReactNode | No | Optional body copy |
href | string | No | When set, the item renders as a link |
icon | Partial<AvatarProps> & { isLoading?: boolean } | No | Avatar image, initials, or loading placeholder |
Code Examplesโ
- Quick Start
- Empty State
- Compound Components
- Block Override
function Example() { const [isNotificationsOpen, setIsNotificationsOpen] = React.useState(false); const [unreadCount, setUnreadCount] = React.useState(10); const notificationsList = [ { title: 'Email updated', text: 'Your email has been updated successfully.', createdAt: new Date().toISOString(), href: '/settings', }, { icon: {children: 'G'}, title: 'Order shipped', text: 'Your order #2847 has been dispatched.', href: '/orders', createdAt: new Date(Date.now() - 86400000).toISOString(), }, ]; return ( <Notifications isNotificationsOpen={isNotificationsOpen} setIsNotificationsOpen={setIsNotificationsOpen} notificationsList={notificationsList} unreadCount={unreadCount} onClick={() => { setUnreadCount(0); setIsNotificationsOpen(!isNotificationsOpen); }} /> ); }
Open the panel with an empty list. Customize the message with emptyStateMessage.
function Example() { const [isNotificationsOpen, setIsNotificationsOpen] = React.useState(false); return ( <Notifications isNotificationsOpen={isNotificationsOpen} setIsNotificationsOpen={setIsNotificationsOpen} notificationsList={[]} heading="Updates" emptyStateMessage="You're all caught up" unreadCount={0} onClick={() => setIsNotificationsOpen(!isNotificationsOpen)} /> ); }
Use child blocks to customize the trigger and panel. Pass content on sub-components instead of the root.
function Example() { const [isNotificationsOpen, setIsNotificationsOpen] = React.useState(false); const [unreadCount, setUnreadCount] = React.useState(5); const notificationsList = [ { title: 'New message', text: 'You have a new message from your team.', createdAt: new Date().toISOString(), href: '#messages', }, ]; return ( <Notifications isNotificationsOpen={isNotificationsOpen} setIsNotificationsOpen={setIsNotificationsOpen} notificationsList={notificationsList} onClick={() => { setUnreadCount(0); setIsNotificationsOpen(!isNotificationsOpen); }} unreadCount={unreadCount} > <Notifications.Button /> <Notifications.List heading="Alerts" emptyStateMessage="No alerts yet" /> </Notifications> ); }
Use function children to override default blocks and order.
function Example() { const [isNotificationsOpen, setIsNotificationsOpen] = React.useState(false); const [unreadCount, setUnreadCount] = React.useState(25); const notificationsList = [ { title: 'Document shared', text: 'A document was shared with you.', createdAt: new Date().toISOString(), }, ]; return ( <Notifications isNotificationsOpen={isNotificationsOpen} setIsNotificationsOpen={setIsNotificationsOpen} notificationsList={notificationsList} sx={{alignItems: 'flex-end'}} onClick={() => { setUnreadCount(0); setIsNotificationsOpen(!isNotificationsOpen); }} unreadCount={unreadCount} > {({defaultBlocks, defaultBlockOrder}) => ({ blocks: { ...defaultBlocks, banner: ( <div style={{display: 'flex', flexDirection: 'column', alignItems: 'flex-end'}}> <div style={{position: 'relative', display: 'inline-flex', marginBottom: 10}}> <div style={{ background: '#eef4ff', border: '1px solid #cddcff', borderRadius: '8px', padding: '8px 12px', fontSize: '14px', }} > Check your updates here </div> <div style={{ position: 'absolute', bottom: -5, right: 14, width: 10, height: 10, background: '#eef4ff', borderRight: '1px solid #cddcff', borderBottom: '1px solid #cddcff', transform: 'rotate(45deg)', }} /> </div> <div style={{marginRight: 10}}>{defaultBlocks.button}</div> </div> ), button: <></>, }, blockOrder: ['banner', ...defaultBlockOrder.filter(key => key !== 'button')], })} </Notifications> ); }
When to use block overrides
Use overrides when you need to change block order, replace the default bell or panel, or inject custom content.
Important Propsโ
Core Propsโ
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
isNotificationsOpen | boolean | Yes | - | Whether the notifications popover is open |
setIsNotificationsOpen | (open: boolean) => void | Yes | - | Updates open state; required for closing on outside click |
onClick | React.MouseEventHandler<HTMLButtonElement> | Yes | - | Bell button click handler; typically toggles open state |
notificationsList | NotificationItem[] | Yes | - | Items rendered in the panel (see shape above) |
unreadCount | number | No | undefined | Badge count on the bell button; values above 50 display as 50+ |
Content Propsโ
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
heading | string | No | 'Notifications' | Panel heading above the list |
emptyStateMessage | string | No | 'No notifications' | Message shown when the list is empty |
isLoading | boolean | No | undefined | When true, shows a loader instead of list items |
Layout and Composition Propsโ
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
anchorEl | HTMLElement | null | No | internal state | Anchor element for popover positioning |
setAnchorEl | (el: HTMLElement | null) => void | No | internal setter | Updates the popover anchor element |
children | BlocksOverride | ReactNode | No | undefined | Compound sub-components or a block override function |
className | string | No | undefined | Class name for the root container (also applies nbb-notifications) |
sx | SxProps | No | undefined | MUI system styles for the root Stack |
Notifications inherits StackProps (except children), so standard layout props are supported on the root. Default block order is button โ list; listItem and loader are default blocks used internally by Notifications.List.
Sub-components read values from context and accept partial MUI props:
Notifications.ButtonโIconButtonprops; defaults to aBadgewrappingNotificationsOutlinedNotifications.ListโStackPropsplus optionalheading,emptyStateMessage,isLoading, andformatRelativeDateTextNotifications.ListItemโStackPropsplustitle,createdAt, optionaltext,href,icon, andformatRelativeDateTextNotifications.LoaderโStackProps; defaults toCircularProgress
formatRelativeDateText defaults to ISO date parsing with date-fns and the Japanese locale, returning ไปๆฅ for notifications created today. Pass a custom formatter on Notifications.List or Notifications.ListItem if your dates use another format or locale.
Default UI Blocksโ
| Block | Built on | Notes |
|---|---|---|
Notifications (root) | Stack rendered as aside | Inline wrapper for the trigger button and popover list |
Notifications.Button | IconButton + Badge | Bell icon with optional unread count badge |
Notifications.List | Popover + Typography | Scrollable panel with heading, dividers, empty state, and loading state |
Notifications.ListItem | Stack + Avatar + Typography | Renders as an anchor when href is set |
Notifications.Loader | Stack + CircularProgress | Centered spinner shown while isLoading is true |
TypeScriptโ
import {Notifications} from '@nodeblocks/frontend-notifications-block';
import type {ComponentProps} from 'react';
type NotificationsProps = ComponentProps<typeof Notifications>;
type NotificationItem = NotificationsProps['notificationsList'][number];
function ActivityNotifications({items, unreadCount}: {items: NotificationItem[]; unreadCount: number}) {
const [isOpen, setIsOpen] = React.useState(false);
return (
<Notifications
isNotificationsOpen={isOpen}
setIsNotificationsOpen={setIsOpen}
notificationsList={items}
heading="Activity"
unreadCount={unreadCount}
onClick={() => setIsOpen(prev => !prev)}
/>
);
}