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"
Install dependencies
npm install @hugeicons/react
Add icons component
Copy the icons wrapper component from icons
Copy the source code
Copy notification-card.tsx to your components directory
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
Unique identifier for the notification
Notification title/heading
Notification message/description
status
'unread' | 'read' | 'archived'
default: "'unread'"
Current status of the notification. Affects styling and visibility of the unread indicator.
Timestamp when the notification was created. Automatically formatted as relative time (e.g., “2m ago”, “3h ago”).
Array of action buttons to display Show NotificationAction properties
Unique identifier for the action
Type of action:
redirect: Navigation action (shows link icon)
api_call: API call action (shows checkmark icon)
workflow: Workflow trigger (shows clock icon)
modal: Opens modal (shows alert icon)
style
'primary' | 'danger' | 'default'
Visual style of the button:
primary: Blue accent
danger: Red accent
default: Gray/muted
Whether the action has been executed. When true, shows checkmark and disables button.
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
ID of the action currently loading. Shows spinner on that action button.
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:
Type Icon Use Case redirectLink Navigate to a page api_callCheckmark Trigger an API call workflowClock Start a workflow modalAlert Open 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.