React notification library for adding customizable toast notifications to applications
—
The Notification Center addon provides persistent notification management through a React hook-based API. Unlike regular toasts that automatically disappear, the notification center maintains a persistent list of notifications that users can manage manually.
The notification center is distributed as a separate addon:
react-toastify/addons/use-notification-centerreact-toastify as a peer dependencyimport { useNotificationCenter } from 'react-toastify/addons/use-notification-center';Main hook for managing persistent notifications with full CRUD operations and state management.
/**
* React hook for managing a persistent notification center
* @param params - Optional configuration parameters
* @returns Notification center API with methods and state
*/
function useNotificationCenter<T = {}>(
params?: UseNotificationCenterParams<T>
): UseNotificationCenter<T>;
interface UseNotificationCenterParams<T = {}> {
/** Initial notifications to populate the center (for rehydration) */
data?: NotificationCenterItem<T>[];
/** Filter function to determine which notifications to include */
filter?: FilterFn<T>;
/** Sort function to order notifications */
sort?: SortFn<T>;
}
type FilterFn<T = {}> = (item: NotificationCenterItem<T>) => boolean;
type SortFn<T = {}> = (a: NotificationCenterItem<T>, b: NotificationCenterItem<T>) => number;
interface UseNotificationCenter<T = {}> {
/** Array of all notifications in the center */
notifications: NotificationCenterItem<T>[];
/** Remove all notifications from the center */
clear(): void;
/** Mark all notifications as read or unread */
markAllAsRead(read?: boolean): void;
/** Mark specific notifications as read or unread */
markAsRead(id: Id | Id[], read?: boolean): void;
/** Remove specific notifications from the center */
remove(id: Id | Id[]): void;
/** Add a new notification to the center */
add(item: NotificationCenterItem<T>): void;
/** Update an existing notification */
update(id: Id, item: Partial<NotificationCenterItem<T>>): void;
/** Find notification(s) by ID */
find(id: Id): NotificationCenterItem<T> | undefined;
/** Sort notifications using a custom comparator */
sort(compareFn: SortFn<T>): void;
/** Number of unread notifications */
unreadCount: number;
}Basic Usage:
import React from 'react';
import { useNotificationCenter } from 'react-toastify/addons/use-notification-center';
function NotificationCenter() {
const {
notifications,
clear,
markAllAsRead,
markAsRead,
remove,
unreadCount
} = useNotificationCenter();
return (
<div className="notification-center">
<div className="header">
<h3>Notifications ({unreadCount} unread)</h3>
<button onClick={() => markAllAsRead()}>Mark All Read</button>
<button onClick={clear}>Clear All</button>
</div>
<div className="notifications">
{notifications.map(notification => (
<div
key={notification.id}
className={`notification ${notification.read ? 'read' : 'unread'}`}
>
<div className="content">{notification.content}</div>
<div className="actions">
<button onClick={() => markAsRead(notification.id)}>
{notification.read ? 'Mark Unread' : 'Mark Read'}
</button>
<button onClick={() => remove(notification.id)}>Remove</button>
</div>
</div>
))}
</div>
</div>
);
}Add notifications to the center programmatically or from toast events.
/**
* Add a new notification to the center
* @param item - The notification item to add
*/
add(item: NotificationCenterItem<T>): void;Manual Addition:
function MyComponent() {
const { add } = useNotificationCenter();
const addNotification = () => {
add({
id: Date.now().toString(),
content: "New notification message",
theme: "light",
type: "info",
read: false,
createdAt: Date.now(),
data: { source: 'manual' }
});
};
return <button onClick={addNotification}>Add Notification</button>;
}From Toast Events:
import { toast } from 'react-toastify';
import { useNotificationCenter } from 'react-toastify/addons/use-notification-center';
function MyComponent() {
const { add } = useNotificationCenter();
const showToastAndAddToCenter = () => {
const toastId = toast.success("Operation completed", {
onClose: () => {
// Add to notification center when toast closes
add({
id: toastId.toString(),
content: "Operation completed successfully",
type: "success",
read: false,
createdAt: Date.now()
});
}
});
};
return <button onClick={showToastAndAddToCenter}>Show Toast</button>;
}Control the read/unread state of notifications individually or in bulk.
/**
* Mark all notifications as read or unread
* @param read - True to mark as read, false for unread (default: true)
*/
markAllAsRead(read?: boolean): void;
/**
* Mark specific notifications as read or unread
* @param id - Single ID or array of IDs to mark
* @param read - True to mark as read, false for unread (default: true)
*/
markAsRead(id: Id | Id[], read?: boolean): void;Usage Examples:
const { markAllAsRead, markAsRead, notifications } = useNotificationCenter();
// Mark all as read
markAllAsRead();
// Mark all as unread
markAllAsRead(false);
// Mark specific notification as read
markAsRead('notification-1');
// Mark multiple notifications as read
markAsRead(['notification-1', 'notification-2']);
// Mark as unread
markAsRead('notification-1', false);
// Toggle read state
const toggleRead = (id: string) => {
const notification = notifications.find(n => n.id === id);
if (notification) {
markAsRead(id, !notification.read);
}
};Remove individual notifications or clear the entire center.
/**
* Remove specific notifications from the center
* @param id - Single ID or array of IDs to remove
*/
remove(id: Id | Id[]): void;
/**
* Remove all notifications from the center
*/
clear(): void;Usage Examples:
const { remove, clear } = useNotificationCenter();
// Remove single notification
remove('notification-1');
// Remove multiple notifications
remove(['notification-1', 'notification-2']);
// Clear all notifications
clear();
// Remove all read notifications
const removeAllRead = () => {
const readIds = notifications
.filter(n => n.read)
.map(n => n.id);
remove(readIds);
};Modify existing notifications in the center.
/**
* Update an existing notification
* @param id - The ID of the notification to update
* @param item - Partial notification item with fields to update
*/
update(id: Id, item: Partial<NotificationCenterItem<T>>): void;Usage Examples:
const { update } = useNotificationCenter();
// Update notification content
update('notification-1', {
content: "Updated message content",
read: true
});
// Update notification data
update('notification-1', {
data: { ...existingData, priority: 'high' }
});
// Update notification theme
update('notification-1', {
theme: 'dark',
type: 'warning'
});Search for specific notifications and customize the display order.
/**
* Find a notification by ID
* @param id - The ID to search for
* @returns The notification item or undefined if not found
*/
find(id: Id): NotificationCenterItem<T> | undefined;
/**
* Sort notifications using a custom comparator function
* @param compareFn - Function to determine sort order
*/
sort(compareFn: SortFn<T>): void;
type SortFn<T> = (a: NotificationCenterItem<T>, b: NotificationCenterItem<T>) => number;Usage Examples:
const { find, sort, notifications } = useNotificationCenter();
// Find specific notification
const notification = find('notification-1');
if (notification) {
console.log('Found:', notification.content);
}
// Sort by creation time (newest first)
sort((a, b) => b.createdAt - a.createdAt);
// Sort by read status (unread first)
sort((a, b) => {
if (a.read === b.read) return 0;
return a.read ? 1 : -1;
});
// Sort by type priority
const typePriority = { error: 0, warning: 1, info: 2, success: 3 };
sort((a, b) => typePriority[a.type] - typePriority[b.type]);Initialize the notification center with custom settings and data.
interface UseNotificationCenterParams<T = {}> {
/** Initial notifications to populate the center */
data?: NotificationCenterItem<T>[];
/** Maximum number of notifications to keep (oldest removed first) */
limit?: number;
/** Filter function to determine which notifications to include */
filter?: FilterFn<T>;
/** Sort function to order notifications */
sort?: SortFn<T>;
}
type FilterFn<T> = (notification: NotificationCenterItem<T>) => boolean;
type SortFn<T> = (a: NotificationCenterItem<T>, b: NotificationCenterItem<T>) => number;Configuration Examples:
// Initialize with data and limit
const { notifications } = useNotificationCenter({
data: [
{
id: '1',
content: 'Welcome notification',
type: 'info',
read: false,
createdAt: Date.now()
}
],
limit: 50,
filter: (notification) => !notification.read, // Only show unread
sort: (a, b) => b.createdAt - a.createdAt // Newest first
});
// Filter for important notifications only
const { notifications: importantNotifications } = useNotificationCenter({
filter: (notification) =>
notification.data?.priority === 'high' || notification.type === 'error'
});Automatically populate the notification center from toast lifecycle events.
import { toast } from 'react-toastify';
import { useNotificationCenter } from 'react-toastify/addons/use-notification-center';
function IntegratedNotifications() {
const { add, notifications } = useNotificationCenter();
// Subscribe to toast events
React.useEffect(() => {
const unsubscribe = toast.onChange((payload) => {
if (payload.status === 'added') {
// Add to notification center when toast appears
add({
id: payload.id.toString(),
content: payload.content,
type: payload.type || 'default',
theme: payload.theme || 'light',
read: false,
createdAt: Date.now(),
data: payload.data
});
}
});
return unsubscribe;
}, [add]);
const showToast = () => {
toast.success("This will appear in both toast and notification center");
};
return (
<div>
<button onClick={showToast}>Show Toast</button>
<div>Notification Center has {notifications.length} items</div>
</div>
);
}interface NotificationCenterItem<T = {}> {
/** Unique identifier for the notification */
id: Id;
/** Content to display (string, React node, or render function) */
content: ToastContent<T>;
/** Timestamp when notification was created */
createdAt: number;
/** Whether the notification has been read */
read?: boolean;
/** Toast type for styling and icons */
type?: TypeOptions;
/** Theme for styling */
theme?: Theme;
/** Custom data associated with the notification */
data?: T;
/** Custom icon for the notification */
icon?: ToastIcon;
}Save notification center state to localStorage:
function usePersistentNotificationCenter<T>() {
const STORAGE_KEY = 'notification-center';
// Load initial data from storage
const loadInitialData = (): NotificationCenterItem<T>[] => {
try {
const stored = localStorage.getItem(STORAGE_KEY);
return stored ? JSON.parse(stored) : [];
} catch {
return [];
}
};
const nc = useNotificationCenter<T>({
data: loadInitialData()
});
// Save to storage whenever notifications change
React.useEffect(() => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(nc.notifications));
}, [nc.notifications]);
return nc;
}Group notifications by category:
interface CategorizedNotification {
category: 'system' | 'user' | 'marketing';
priority: 'low' | 'medium' | 'high';
}
function CategorizedNotificationCenter() {
const { notifications, add } = useNotificationCenter<CategorizedNotification>();
const notificationsByCategory = React.useMemo(() => {
return notifications.reduce((acc, notification) => {
const category = notification.data?.category || 'system';
if (!acc[category]) acc[category] = [];
acc[category].push(notification);
return acc;
}, {} as Record<string, NotificationCenterItem<CategorizedNotification>[]>);
}, [notifications]);
return (
<div>
{Object.entries(notificationsByCategory).map(([category, items]) => (
<div key={category}>
<h4>{category} ({items.length})</h4>
{items.map(item => (
<div key={item.id}>{item.content}</div>
))}
</div>
))}
</div>
);
}Install with Tessl CLI
npx tessl i tessl/npm-react-toastify