Skip to main content
The Notification Card displays notifications with support for action buttons, read states, and time formatting. Migrated from GAIA’s EnhancedNotificationCard.

Preview

import { NotificationCard } from "@/components/ui/notification-card";

export default function Example() {
  return (
    <NotificationCard
      id="notif-1"
      title="New message from Sarah"
      body="Can you review the design mockups I sent?"
      status="unread"
      createdAt={new Date()}
    />
  );
}

Installation

npx shadcn@latest add "https://ui.heygaia.io/r/notification-card"

Usage

Basic Notification

import { NotificationCard } from "@/components/ui/notification-card";

<NotificationCard
  id="notif-1"
  title="System Update"
  body="Your system has been updated to the latest version."
  status="unread"
  createdAt={new Date()}
/>

With Mark as Read

import { NotificationCard } from "@/components/ui/notification-card";
import { useState } from "react";

function NotificationList() {
  const [notifications, setNotifications] = useState([
    { id: "1", title: "New message", body: "...", status: "unread" }
  ]);

  const markAsRead = (id) => {
    setNotifications(prev => 
      prev.map(n => n.id === id ? { ...n, status: "read" } : n)
    );
  };

  return (
    <NotificationCard
      {...notifications[0]}
      onMarkAsRead={markAsRead}
    />
  );
}

With Actions

import { NotificationCard } from "@/components/ui/notification-card";
import { useState } from "react";

function NotificationWithActions() {
  const [loadingActionId, setLoadingActionId] = useState(null);

  const actions = [
    {
      id: "approve",
      label: "Approve",
      type: "api_call",
      style: "primary"
    },
    {
      id: "reject",
      label: "Reject",
      type: "api_call",
      style: "danger"
    }
  ];

  const handleAction = async (notifId, actionId, type) => {
    setLoadingActionId(actionId);
    
    // Simulate API call
    await new Promise(resolve => setTimeout(resolve, 1000));
    
    if (type === "redirect") {
      window.location.href = `/action/${actionId}`;
    }
    
    setLoadingActionId(null);
  };

  return (
    <NotificationCard
      id="notif-1"
      title="Approval Required"
      body="A new document needs your approval."
      status="unread"
      actions={actions}
      onAction={handleAction}
      loadingActionId={loadingActionId}
    />
  );
}

Props

id
string
required
Unique identifier for the notification
title
string
required
Notification title/heading
body
string
required
Notification message/description
status
'unread' | 'read' | 'archived'
default:"'unread'"
Current status of the notification. Affects styling and visibility of the unread indicator.
createdAt
string | Date
Timestamp when the notification was created. Automatically formatted as relative time (e.g., “2m ago”, “3h ago”).
actions
NotificationAction[]
Array of action buttons to display
onMarkAsRead
(id: string) => void
Callback when the mark as read button is clicked. Only shown for unread notifications.
onAction
(notificationId: string, actionId: string, actionType: ActionType) => void
Callback when an action button is clicked
loadingActionId
string
ID of the action currently loading. Shows spinner on that action button.
className
string
Additional CSS classes to apply to the card

Features

Status Indicators

  • Unread: Blue dot indicator, darker background
  • Read: No indicator, lighter background, muted text
  • Archived: (Same as read, typically filtered from main list)

Time Formatting

Automatically formats timestamps:
  • “Just now” - Less than 1 minute
  • “5m ago” - Less than 1 hour
  • “3h ago” - Less than 24 hours
  • “2d ago” - Less than 7 days
  • “Mar 1” - Older than 7 days

Action Types

Each action type has a specific icon:
TypeIconUse Case
redirectLinkNavigate to a page
api_callCheckmarkTrigger an API call
workflowClockStart a workflow
modalAlertOpen a modal/dialog

Action Styles

  • Primary: Blue background, used for main actions
  • Danger: Red background, used for destructive actions
  • Default: Gray background, used for secondary actions

Loading States

When an action is loading:
  • Spinner appears on the button
  • Button becomes semi-transparent
  • Other actions remain clickable (unless type is modal)

Executed Actions

When an action is marked as executed:
  • Checkmark icon appears
  • Button is disabled
  • Button has reduced opacity

Examples

Approval Workflow

const approvalNotification = {
  id: "approval-1",
  title: "Expense Report Pending",
  body: "John submitted an expense report for $1,250 that requires your approval.",
  status: "unread",
  createdAt: "2024-03-03T14:30:00Z",
  actions: [
    {
      id: "approve",
      label: "Approve",
      type: "api_call",
      style: "primary"
    },
    {
      id: "review",
      label: "Review Details",
      type: "redirect",
      style: "default"
    },
    {
      id: "reject",
      label: "Reject",
      type: "api_call",
      style: "danger"
    }
  ]
};

<NotificationCard {...approvalNotification} onAction={handleApproval} />

Simple Alert

const alertNotification = {
  id: "alert-1",
  title: "Deployment Successful",
  body: "Your changes have been deployed to production.",
  status: "unread",
  createdAt: new Date(Date.now() - 300000), // 5 minutes ago
  actions: [
    {
      id: "view",
      label: "View Deployment",
      type: "redirect",
      style: "primary"
    }
  ]
};

<NotificationCard {...alertNotification} />

Multi-step Workflow

const workflowNotification = {
  id: "workflow-1",
  title: "Code Review Requested",
  body: "Sarah requested your review on PR #142: Update authentication flow",
  status: "unread",
  createdAt: "2024-03-03T09:15:00Z",
  actions: [
    {
      id: "start-review",
      label: "Start Review",
      type: "workflow",
      style: "primary"
    },
    {
      id: "view-pr",
      label: "View PR",
      type: "redirect"
    },
    {
      id: "skip",
      label: "Skip",
      type: "api_call"
    }
  ]
};

<NotificationCard {...workflowNotification} onAction={handleWorkflow} />

Styling

The notification card uses:
  • Zinc color palette for light/dark mode
  • Rounded corners (rounded-2xl)
  • Subtle background transitions between read/unread
  • Hover effects on action buttons
  • Smooth transitions for all state changes

Accessibility

  • Semantic HTML structure
  • ARIA labels on icon-only buttons
  • Keyboard navigation support
  • Clear visual indicators for all states
  • Screen reader friendly time formatting
Group notifications by status (unread first) and sort by createdAt for the best user experience.
Modal-type actions don’t show loading states because the modal typically handles its own loading UI.

Build docs developers (and LLMs) love