Skip to main content

Documentation

NotificationList

Container for displaying a list of notifications with empty states and loading skeletons

Tags: component notification list container

View Demo

Container component for displaying a list of notifications. Handles empty states, loading skeletons, and renders NotificationItem components.

Import​

Notification components are available via a separate entry point to make @classic-homes/notifications an optional dependency.

<script>
  import { NotificationList } from '@classic-homes/theme-svelte/notifications';
</script>

Note: Using notification components requires @classic-homes/notifications to be installed.

Basic Usage​

<script>
  import type { Notification } from '@classic-homes/notifications/core';

  let notifications = $state<Notification[]>([
    // ... notification objects
  ]);
</script>

<NotificationList {notifications} />

Props​

Prop Type Default Description
notifications Notification[] required List of notifications to display
selectedIds Set<string> new Set() Set of selected notification IDs
selectable boolean false Whether selection mode is enabled
onToggleSelect (id: string, selected: boolean) => void - Callback when a notification's selection changes
onNotificationClick (notification: Notification) => void - Callback when a notification is clicked
emptyState Snippet - Custom empty state snippet
emptyTitle string 'No notifications' Empty state title
emptyDescription string "You're all caught up!" Empty state description
loading boolean false Whether the list is loading
class string - Additional CSS classes

Empty State​

The default empty state shows a bell-off icon with customizable text:

<NotificationList
  notifications={[]}
  emptyTitle="No unread notifications"
  emptyDescription="All caught up!"
/>

Custom Empty State​

Provide a completely custom empty state:

<NotificationList notifications={[]}>
  {#snippet emptyState()}
    <div class="text-center py-12">
      <img src="/empty-inbox.svg" alt="" class="mx-auto mb-4" />
      <h3 class="text-lg font-medium">Inbox Zero!</h3>
      <p class="text-muted-foreground">You have no notifications</p>
    </div>
  {/snippet}
</NotificationList>

Loading State​

Show skeleton placeholders while loading:

<NotificationList notifications={[]} loading={true} />

The loading state displays 3 animated skeleton items.

Selection Mode​

Enable selection for bulk operations:

<script>
  let selectedIds = $state(new Set<string>());

  function handleToggleSelect(id: string, selected: boolean) {
    const newSet = new Set(selectedIds);
    if (selected) {
      newSet.add(id);
    } else {
      newSet.delete(id);
    }
    selectedIds = newSet;
  }
</script>

<NotificationList
  {notifications}
  {selectedIds}
  selectable={true}
  onToggleSelect={handleToggleSelect}
/>

Click Handler​

Handle notification clicks to show details:

<script>
  let selectedNotification = $state<Notification | null>(null);
  let dialogOpen = $state(false);

  function handleNotificationClick(notification: Notification) {
    selectedNotification = notification;
    dialogOpen = true;
  }
</script>

<NotificationList {notifications} onNotificationClick={handleNotificationClick} />

<NotificationDialog notification={selectedNotification} bind:open={dialogOpen} />

Examples​

Complete Notification Center​

<script>
  import {
    NotificationList,
    NotificationFilters,
    NotificationBulkActions,
    NotificationDialog,
  } from '@classic-homes/theme-svelte/notifications';
  import type { Notification, NotificationFilter, NotificationCounts } from '@classic-homes/notifications/core';

  let notifications = $state<Notification[]>([...]);
  let filter = $state<NotificationFilter>('all');
  let selectedIds = $state(new Set<string>());
  let dialogOpen = $state(false);
  let selectedNotification = $state<Notification | null>(null);

  const filteredNotifications = $derived(() => {
    switch (filter) {
      case 'unread': return notifications.filter(n => !n.read);
      case 'read': return notifications.filter(n => n.read);
      default: return notifications;
    }
  });

  const counts: NotificationCounts = $derived({
    all: notifications.length,
    unread: notifications.filter(n => !n.read).length,
    read: notifications.filter(n => n.read).length,
  });
</script>

<NotificationFilters bind:filter {counts} />

<NotificationBulkActions
  selectedCount={selectedIds.size}
  totalCount={filteredNotifications().length}
  onSelectAll={(selected) => {
    /* ... */
  }}
  onMarkRead={() => {
    /* ... */
  }}
  onDelete={() => {
    /* ... */
  }}
/>

<NotificationList
  notifications={filteredNotifications()}
  {selectedIds}
  selectable={true}
  onToggleSelect={handleToggleSelect}
  onNotificationClick={handleNotificationClick}
/>

<NotificationDialog notification={selectedNotification} bind:open={dialogOpen} />

With Filter-Specific Empty States​

<NotificationList
  notifications={filteredNotifications()}
  emptyTitle={filter === 'unread'
    ? 'No unread notifications'
    : filter === 'read'
      ? 'No read notifications'
      : 'No notifications'}
  emptyDescription={filter === 'unread'
    ? 'All caught up!'
    : filter === 'read'
      ? 'No notifications have been read yet'
      : "You're all caught up!"}
/>
On this page