Bela UI

Notification Item

Bela UI Notification Item component for alerts, activity updates, and inbox messages.

Preview

New comment on your post

Sarah commented: 'This design looks amazing!'

Your deployment succeeded

Production deployment completed successfully.

New follower

Alex started following you.

src/components/notification-item-demo.tsx
import {
  type Notification,
  NotificationItem,
} from "@/components/bela-ui/notification-item";

export default function NotificationItemDemo() {
  return (
    <div className="overflow-hidden rounded-lg border border-border bg-card divide-y divide-border">
      {notifications.map((notification) => (
        <NotificationItem key={notification.id} notification={notification} />
      ))}
    </div>
  );
}

// sample data for demonstration purposes

export const notifications: Notification[] = [
  {
    id: "1",
    title: "New comment on your post",
    body: "Sarah commented: 'This design looks amazing!'",
    actionUrl: null,
    readAt: null,
    archivedAt: null,
    createdAt: "2026-06-09T09:12:00.000Z",
  },
  {
    id: "2",
    title: "Your deployment succeeded",
    body: "Production deployment completed successfully.",
    actionUrl: null,
    readAt: "2026-06-09T06:35:00.000Z",
    archivedAt: null,
    createdAt: "2026-06-09T05:48:00.000Z",
  },
  {
    id: "3",
    title: "New follower",
    body: "Alex started following you.",
    actionUrl: null,
    readAt: null,
    archivedAt: null,
    createdAt: "2026-06-08T18:20:00.000Z",
  },
];

Installation

npx bela-ui add notification-item
pnpm dlx bela-ui add notification-item
yarn dlx bela-ui add notification-item
bunx bela-ui add notification-item
  1. Copy the source code into your project.
src/components/bela-ui/notification-item.tsx
"use client";

import Link from "next/link";
import * as React from "react";
import { Button } from "@/components/ui/button";

export type Notification = {
  id: string;
  createdAt: string;
  title: string;
  body: string | null;
  actionUrl: string | null;
  readAt: string | null;
  archivedAt: string | null;
};

export interface NotificationItemProps {
  notification: Notification;
}

export const NotificationItem = React.memo(function NotificationItem({
  notification,
}: NotificationItemProps) {
  const unread = notification.readAt === null;

  const content = (
    <>
      <div className="mt-1 shrink-0">
        <div
          className={`size-2 rounded-full ${
            unread ? "bg-sky-600 dark:bg-sky-400" : "bg-border"
          }`}
        />
      </div>

      <div className="min-w-0 flex-1">
        <div className="flex items-start justify-between gap-4">
          <h3
            className={
              unread
                ? "truncate text-sm font-semibold text-foreground"
                : "truncate text-sm font-medium text-foreground/80"
            }
          >
            {notification.title}
          </h3>

          <time className="shrink-0 text-xs text-muted-foreground">
            {formatRelativeDate(notification.createdAt)}
          </time>
        </div>

        {notification.body && (
          <p className="mt-1 line-clamp-2 text-sm text-muted-foreground">
            {notification.body}
          </p>
        )}
      </div>
    </>
  );

  return (
    <div className="group p-3 transition-colors hover:bg-accent/60">
      {notification.actionUrl ? (
        <Link
          href={notification.actionUrl}
          className="flex w-full gap-3 text-left transition-colors"
        >
          {content}
        </Link>
      ) : (
        <div className="flex w-full gap-3 text-left transition-colors">
          {content}
        </div>
      )}

      <div className="ml-5 mt-2 flex items-center gap-1">
        {!notification.readAt && (
          <Button size="sm" variant="secondary" className="h-fit px-2 py-1">
            Mark as read
          </Button>
        )}

        {!notification.archivedAt && (
          <Button size="sm" variant="outline" className="h-fit px-2 py-1">
            Archive
          </Button>
        )}
      </div>
    </div>
  );
});

NotificationItem.displayName = "NotificationItem";

// ---

function formatRelativeDate(date: string | Date): string {
  const target = new Date(date);
  const targetTime = target.getTime();

  if (Number.isNaN(targetTime)) {
    return "unknown time";
  }

  const now = new Date();
  const diffMs = targetTime - now.getTime();
  const absDiffMs = Math.abs(diffMs);
  const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });

  const minute = 60 * 1000;
  const hour = 60 * minute;
  const day = 24 * hour;
  const week = 7 * day;
  const month = 30 * day;

  if (absDiffMs < minute) {
    return "just now";
  }

  if (absDiffMs < hour) {
    return rtf.format(Math.round(diffMs / minute), "minute");
  }

  if (absDiffMs < day) {
    return rtf.format(Math.round(diffMs / hour), "hour");
  }

  if (absDiffMs < week) {
    return rtf.format(Math.round(diffMs / day), "day");
  }

  if (absDiffMs < 26 * week) {
    return rtf.format(Math.round(diffMs / week), "week");
  }

  return rtf.format(Math.round(diffMs / month), "month");
}
  1. Update the import paths to match your project setup.

Usage

src/components/notification-item-demo.tsx
import {
  type Notification,
  NotificationItem,
} from "@/components/bela-ui/notification-item";

export default function NotificationItemDemo() {
  return (
    <div className="overflow-hidden rounded-lg border border-border bg-card divide-y divide-border">
      {notifications.map((notification) => (
        <NotificationItem key={notification.id} notification={notification} />
      ))}
    </div>
  );
}

// sample data for demonstration purposes

export const notifications: Notification[] = [
  {
    id: "1",
    title: "New comment on your post",
    body: "Sarah commented: 'This design looks amazing!'",
    actionUrl: null,
    readAt: null,
    archivedAt: null,
    createdAt: "2026-06-09T09:12:00.000Z",
  },
  {
    id: "2",
    title: "Your deployment succeeded",
    body: "Production deployment completed successfully.",
    actionUrl: null,
    readAt: "2026-06-09T06:35:00.000Z",
    archivedAt: null,
    createdAt: "2026-06-09T05:48:00.000Z",
  },
  {
    id: "3",
    title: "New follower",
    body: "Alex started following you.",
    actionUrl: null,
    readAt: null,
    archivedAt: null,
    createdAt: "2026-06-08T18:20:00.000Z",
  },
];

Props

Notification

PropertyTypeDescription
idstringUnique notification identifier.
titlestringNotification title.
bodystring | nullOptional notification content.
actionUrlstring | nullOptional URL to navigate to when clicked.
readAtstring | nullRead timestamp. null indicates unread.
archivedAtstring | nullArchive timestamp.
createdAtstringNotification creation timestamp.

On this page