Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Feedback

Notification Center

Feedback

Notification Center React component for Tailwind CSS v4

Notification BellProgress
Live props
State
defaultFilter
loading
hasMore
defaultOpen
Content
title
markAllLabel
emptyTitle
emptyText
count
settingsHref
Layout
groupByDay
width400px
side
align
Appearance
timeFormat
tone
badgeTone
max99
badgeRingColor
Behavior
ringOnNew
swipeActions
responsive
shortcut
locale
Code
import { NotificationCenter } from "@/components/ui/notification-center"

<NotificationCenter />
  1. Home
  2. Components
  3. Feedback
  4. Notification Center
Pro

Notification Center React component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React notification center: a bell with an unread count that opens an inbox with tabs, day groups, inline actions and swipe to dismiss.

Click the bell (or press its shortcut) and the panel grows out of it with All / Unread / Mentions tabs, the Unread tab counting live. Notifications sit under sticky day labels (Today, Yesterday, weekdays, then dates) with relative times that refresh every minute. Click a row to open it: it turns read, follows its link and the panel closes. Inline buttons (accept an invite, retry a payment) spin while their promise runs. The ⋯ menu on each row (on hover or focus with a mouse, always visible on touch) marks it read or unread or dismisses it, and the row collapses out. "Mark all as read" clears every dot at once and the red count on the bell scales away; when a new notification arrives the bell swings and the row slides in, and screen readers hear it through a polite live region. Keyboard: Tab into the panel, Down or Home jumps to the first row, Up / Down move between rows, Home / End jump, Enter opens, Escape closes and focus returns to the bell. The last row loads more by itself when it scrolls into view. On a phone the panel is a full-height bottom sheet: swipe a row left to dismiss it (a red Dismiss layer follows your finger) or right to toggle read, with a haptic tick where supported; drag the handle down to close.

Notification Center is a Pro React component in the React feedback components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add notification-center, together with Avatar, Badge, Button, Drawer, Dropdown Menu and 12 more and the npm packages @radix-ui/react-popover, lucide-react and motion.

Add to a project

Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup

npx wingo-ui@latest add notification-center

Notification Center is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans

Usage

Import NotificationCenter from @/components/ui/notification-center.

import { CreditCard } from "lucide-react"
import { NotificationCenter, NotificationList, type NotificationItem } from "@/components/ui/notification-center"

const notifications: NotificationItem[] = [
  {
    id: "n1",
    type: "mention",
    name: "Sophia Lee",
    avatar: "/demo/ioana-dumitrescu.jpg",
    title: "Sophia Lee mentioned you in “Acme Corp quote”",
    body: "@Emma Carter can you check the discount before we send it?",
    createdAt: "2026-09-23T10:42:00",
    href: "/quotes/Q-2026-118",
  },
  {
    id: "n2",
    icon: <CreditCard />,
    tone: "danger",
    title: "Your Pro plan payment failed",
    body: "The card ending in 4821 was declined.",
    createdAt: "2026-09-23T08:15:00",
    actions: [{ label: "Retry", onSelect: () => fetch("/api/billing/retry", { method: "POST" }) }],
  },
]

export function TopbarNotifications() {
  return (
    <NotificationCenter
      notifications={notifications}
      onRead={(ids) => console.log("read", ids)}
      onDismiss={(id) => console.log("dismiss", id)}
    />
  )
}

// the panel without the bell (full inbox page, sidebar panel): same content props, a header when title is set.
// it paints on var(--background); on a raised panel set [--nc-surface:var(--surface)] in className
export function InboxPage() {
  return <NotificationList title="Notifications" notifications={notifications} className="rounded-3xl border border-border" />
}

Props

  • notificationsdefault "[]"
    NotificationItem[]

    Newest first. { id, title: ReactNode, body?, createdAt: Date | string | number, read?, type?, avatar?, name?, icon?, tone?, href?, actions?: NotificationAction[] }. NotificationAction is { label, onSelect: () => void | Promise<unknown>, variant?: "solid" | "soft" | "ghost", tone? }. String dates are parsed; invalid ones go last without a time. type "mention" feeds the Mentions tab, and @Name inside string titles and bodies is highlighted.

  • onRead
    (ids: string[]) => void

    Rows turned read: from the row menu, a swipe right, opening a row or Mark all (when onReadAll is not set). The panel updates at once and keeps the change until your data agrees.

  • onUnread
    (ids: string[]) => void

    Rows turned unread from the row menu or a swipe right on a read row.

  • onReadAll
    () => void

    "Mark all as read" in the header (shown while anything is unread). Without it onRead gets every unread id.

  • onDismiss
    (id: string) => void

    A row was dismissed (row menu or swipe left). Runs after the row has collapsed out, so remove it from your data here.

  • onSelect
    (item: NotificationItem) => void

    A row was opened: it turns read, navigates to its href through NavLink and the panel closes.

  • filters
    NotificationFilter[]

    The tabs, All / Unread / Mentions by default: { value, label, filter?: (item) => boolean, badge?, emptyTitle?, emptyText? }. A tab whose value is "unread" shows the unread count; each tab can have its own empty state. Fewer than two filters hide the tabs.

  • filter
    string

    The active tab, controlled. Pair it with onFilterChange.

  • defaultFilterdefault "all"
    string

    The tab it starts on while uncontrolled (a filter value).

  • onFilterChange
    (filter: string) => void

    Fires when another tab is picked.

  • titledefault "Notifications"
    ReactNode

    The panel title; it also names the dialog.

  • markAllLabeldefault "Mark all as read"
    string

    The header button that clears every unread dot.

  • emptyTitledefault "You're all caught up"
    string

    The empty state of a tab with nothing in it. A filter's own emptyTitle wins.

  • emptyTextdefault ""
    string

    A second, muted line under the empty title.

  • emptyIcon
    ReactNode

    Replaces the BellOff icon of the empty state.

  • groupByDaydefault true
    boolean

    Sticky day labels: Today, Yesterday, weekday names this week, then dates in the locale. Off, rows show the date with the time.

  • timeFormatdefault "relative"
    "relative" | "time"

    "5 min ago" through Intl.RelativeTimeFormat, refreshed every minute while open, or the clock time ("14:20"). Under an older day label rows show the clock time, so "Yesterday" is never said twice. Relative times are computed after mount: the server and hydration render print the clock time, so there is no mismatch.

  • tonedefault "neutral"
    "neutral" | "brand" | "success" | "warning" | "danger" | "info"

    The unread dots and the tab indicator.

  • badgeTonedefault "danger"
    "neutral" | "brand" | "success" | "warning" | "danger" | "info"

    The count badge on the bell. Red by default on purpose: people read a red count as unread.

  • maxdefault 99
    number

    Counts above it read "99+".

  • widthdefault 400
    number

    The desktop panel width in px (never wider than the screen minus 24px).

  • badgeRingColordefault "var(--bar-bg, var(--background))"
    string

    The 2px ring that cuts the badge out of the bell: the surface the bell sits on.

  • countdefault ""
    number

    Overrides the badge number (default: the unread count). 0 hides the badge. The playground's empty control means left out.

  • loadingdefault false
    boolean

    Skeleton rows while the list is empty, a spinner row after the last one otherwise.

  • hasMoredefault false
    boolean

    Shows the "Load more" row at the end, which also loads by itself when it scrolls into view.

  • onLoadMore
    () => (void | Promise<unknown>)

    Loads the next page. A returned promise keeps a spinner in the row until it settles.

  • defaultOpendefault false
    boolean

    Starts open (uncontrolled).

  • open
    boolean

    Controlled open state. Pair it with onOpenChange.

  • onOpenChange
    (open: boolean) => void

    Fires when the panel opens or closes.

  • ringOnNewdefault true
    boolean

    The bell swings from its top when the unread count goes up (never under reduced motion).

  • swipeActionsdefault true
    boolean

    Swipe a row left to dismiss it, right to toggle read. Follows the finger and commits past a quarter of the row or on a flick.

  • responsivedefault true
    boolean

    Below 768px the panel is a full-height bottom sheet with full-width tabs.

  • sidedefault "bottom"
    "top" | "right" | "bottom" | "left"

    Where the desktop panel opens; it flips when there is no room.

  • aligndefault "end"
    "start" | "center" | "end"

    The panel's alignment against the bell. end suits a bell at the right of a top bar.

  • shortcutdefault ""
    string

    A key combo in mod+k notation that opens and closes the panel from anywhere (none by default).

  • localedefault ""
    string

    Dates, weekdays and relative times. Empty uses the LocaleProvider or the browser.

  • settingsHrefdefault ""
    string

    Adds a gear link in the header (a NavLink), e.g. to notification settings.

  • onSettings
    () => void

    Adds a gear button in the header, or runs before the settingsHref link.

  • trigger
    ReactNode

    A custom trigger instead of the bell, rendered with asChild (one element that takes a ref).

  • linkComponent
    (props: LinkComponentProps) => ReactNode

    The router link for rows with href and the settings link; default from LinkProvider.

  • container
    HTMLElement | null

    Portal target for the panel and the sheet (default document.body).

  • labels
    { trigger?: (unread) => string; markRead?; markUnread?; dismiss?; more?; loadMore?; loading?; settings?; filters?; unread?; earlier?; newNotification?: (title) => string }

    Every other string, English by default: the bell's label ("Notifications, 3 unread"), the row menu, the swipe layers, load more, the live announcement for new notifications.

  • classNames
    Partial<Record<"trigger" | "badge" | "content" | "header" | "title" | "markAll" | "settings" | "tabs" | "list" | "group" | "groupLabel" | "item" | "media" | "body" | "time" | "actions" | "empty" | "footer", string>>

    Extra classes for the inner parts.

  • className
    string

    Extra classes for the bell button. Other props (aria-*, data-*, handlers) go to the bell too.

Parts

  • NotificationList

    component

    The inbox without the bell, for a full notifications page or a sidebar panel: the same tabs, day groups, rows, actions, empty state and load more. Takes every list prop of NotificationCenter (notifications, filters, onRead, onDismiss, labels...) plus div props.

  • NotificationItem

    type

    One notification: { id, title, body?, createdAt, read?, type?, avatar?, name?, icon?, tone?, href?, actions? }. type "mention" feeds the Mentions tab.

  • NotificationAction

    type

    An inline button on a row: { label, onSelect, variant?, tone? }. A returned promise keeps it spinning until it settles.

  • NotificationFilter

    type

    A tab: { value, label, filter?, badge?, emptyTitle?, emptyText? }.

Examples

Phone sheet with swipe actions

The full-height sheet a phone gets: swipe left to delete, right to mark read.

Empty states per tab

Each filter brings its own empty title and text; everything here is already read.

Custom filters

All / Invoices / Team with a count on the invoices tab.

Inbox page

NotificationList on its own, with a settings link and pages that load as you scroll.

Time format and locale

Relative US times next to 24-hour British clock times.

Tones

Brand and info unread dots and badges on a raised bar.

Loading

Skeleton rows on the first load, then the list fades in.

Dependencies

@radix-ui/react-popoverlucide-reactmotionavatarbadgebuttondrawerdropdown-menuskeletonspinnertabsuse-controllable-stateuse-localeuse-media-queryuse-shortcutdate-utilsmotionnavigationtonesui-config

Files

  • components/ui/notification-center.tsx

FAQ

Is the React Notification Center component free?

The preview, props and docs are free; copying the source needs a Pro plan. See plans.

How do I install Notification Center?

Run npx wingo-ui@latest add notification-center. It also adds the Wingo UI items it builds on (Avatar, Badge, Button, Drawer, Dropdown Menu and 12 more) and the npm packages @radix-ui/react-popover, lucide-react and motion. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/notification-center.

How does Notification Center behave on phones?

Below 768px the panel is a full-height bottom sheet with full-width tabs. The responsive prop controls this and is on by default.

See also

Notification BellBadgeTabsPopoverDrawer

Used in

Admin Shell

More React feedback components

Setup ChecklistSpinnerSkeletonRoute ProgressEmoji ReactionAlert
All 13 React feedback components

Articles using this component

  • React Notification Center: Inbox, Bell and Toasts

    Component guides · Oct 19, 2026 · 8 min read

PreviousNotification BellNext Progress
NNorthwind CRM
Search clients, invoices, quotes
Northwind CRM · Home

Good morning, Emma

Collected in Sep$38,400.00+12% vs August
Open quotes123 new this week
Active clients1,284+38 this month

Critical alerts

You're all caught up

Open the bell, then add a notification: the bell swings and the row slides in. On a phone, swipe rows.

side="bottom" align="end" max=99 hasMore=false responsive=true swipeActions=true ringOnNew=true