diff --git a/web/src/lib/components/shared-components/notification/__tests__/notification-card.spec.ts b/web/src/lib/components/shared-components/notification/__tests__/notification-card.spec.ts
index 179f1c2f62..76b9c39564 100644
--- a/web/src/lib/components/shared-components/notification/__tests__/notification-card.spec.ts
+++ b/web/src/lib/components/shared-components/notification/__tests__/notification-card.spec.ts
@@ -10,7 +10,7 @@ describe('NotificationCard component', () => {
     vi.spyOn(window, 'clearTimeout');
 
     sut = render(NotificationCard, {
-      notificationInfo: {
+      notification: {
         id: 1234,
         message: 'Notification message',
         timeout: 1000,
@@ -25,7 +25,7 @@ describe('NotificationCard component', () => {
 
   it('shows message and title', () => {
     sut = render(NotificationCard, {
-      notificationInfo: {
+      notification: {
         id: 1234,
         message: 'Notification message',
         timeout: 1000,
diff --git a/web/src/lib/components/shared-components/notification/__tests__/notification-list.spec.ts b/web/src/lib/components/shared-components/notification/__tests__/notification-list.spec.ts
index f8472a3ced..44634d6b20 100644
--- a/web/src/lib/components/shared-components/notification/__tests__/notification-list.spec.ts
+++ b/web/src/lib/components/shared-components/notification/__tests__/notification-list.spec.ts
@@ -12,11 +12,14 @@ describe('NotificationList component', () => {
   const sut: RenderResult<NotificationList> = render(NotificationList);
 
   beforeAll(() => {
-    vi.useFakeTimers();
+    // https://testing-library.com/docs/svelte-testing-library/faq#why-arent-transition-events-running
+    vi.stubGlobal('requestAnimationFrame', (fn: FrameRequestCallback) => {
+      setTimeout(() => fn(Date.now()), 16);
+    });
   });
 
   afterAll(() => {
-    vi.useRealTimers();
+    vi.unstubAllGlobals();
   });
 
   it('shows a notification when added and closes it automatically after the delay timeout', async () => {
@@ -25,18 +28,14 @@ describe('NotificationList component', () => {
     notificationController.show({
       message: 'Notification',
       type: NotificationType.Info,
-      timeout: 3000,
+      timeout: 1,
     });
 
     await waitFor(() => expect(_getNotificationListElement(sut)).toBeInTheDocument());
+    await waitFor(() => expect(_getNotificationListElement(sut)?.children).toHaveLength(1));
+    expect(get(notificationController.notificationList)).toHaveLength(1);
 
-    expect(_getNotificationListElement(sut)?.children).toHaveLength(1);
-
-    vi.advanceTimersByTime(4000);
-    // due to some weirdness in svelte (or testing-library) need to check if it has been removed from the store to make sure it works.
+    await waitFor(() => expect(_getNotificationListElement(sut)).not.toBeInTheDocument());
     expect(get(notificationController.notificationList)).toHaveLength(0);
-
-    // TODO: investigate why this element is not removed from the DOM even notification list is in fact 0.
-    // await waitFor(() => expect(_getNotificationListElement(sut)).not.toBeInTheDocument());
   });
 });
diff --git a/web/src/lib/components/shared-components/notification/notification-card.svelte b/web/src/lib/components/shared-components/notification/notification-card.svelte
index ef855c90a2..a81146692e 100644
--- a/web/src/lib/components/shared-components/notification/notification-card.svelte
+++ b/web/src/lib/components/shared-components/notification/notification-card.svelte
@@ -2,77 +2,47 @@
   import { fade } from 'svelte/transition';
   import Icon from '$lib/components/elements/icon.svelte';
   import {
-    ImmichNotification,
+    type Notification,
     notificationController,
     NotificationType,
   } from '$lib/components/shared-components/notification/notification';
   import { onMount } from 'svelte';
   import { mdiCloseCircleOutline, mdiInformationOutline, mdiWindowClose } from '@mdi/js';
 
-  export let notificationInfo: ImmichNotification;
+  export let notification: Notification;
 
-  let infoPrimaryColor = '#4250AF';
-  let errorPrimaryColor = '#E64132';
-  let warningPrimaryColor = '#D08613';
+  $: icon = notification.type === NotificationType.Error ? mdiCloseCircleOutline : mdiInformationOutline;
 
-  $: icon = notificationInfo.type === NotificationType.Error ? mdiCloseCircleOutline : mdiInformationOutline;
-
-  $: backgroundColor = () => {
-    if (notificationInfo.type === NotificationType.Info) {
-      return '#E0E2F0';
-    }
-
-    if (notificationInfo.type === NotificationType.Error) {
-      return '#FBE8E6';
-    }
-
-    if (notificationInfo.type === NotificationType.Warning) {
-      return '#FFF6DC';
-    }
+  const backgroundColor: Record<NotificationType, string> = {
+    [NotificationType.Info]: '#E0E2F0',
+    [NotificationType.Error]: '#FBE8E6',
+    [NotificationType.Warning]: '#FFF6DC',
   };
 
-  $: borderStyle = () => {
-    if (notificationInfo.type === NotificationType.Info) {
-      return '1px solid #D8DDFF';
-    }
-
-    if (notificationInfo.type === NotificationType.Error) {
-      return '1px solid #F0E8E7';
-    }
-
-    if (notificationInfo.type === NotificationType.Warning) {
-      return '1px solid #FFE6A5';
-    }
+  const borderColor: Record<NotificationType, string> = {
+    [NotificationType.Info]: '#D8DDFF',
+    [NotificationType.Error]: '#F0E8E7',
+    [NotificationType.Warning]: '#FFE6A5',
   };
 
-  $: primaryColor = () => {
-    if (notificationInfo.type === NotificationType.Info) {
-      return infoPrimaryColor;
-    }
-
-    if (notificationInfo.type === NotificationType.Error) {
-      return errorPrimaryColor;
-    }
-
-    if (notificationInfo.type === NotificationType.Warning) {
-      return warningPrimaryColor;
-    }
+  const primaryColor: Record<NotificationType, string> = {
+    [NotificationType.Info]: '#4250AF',
+    [NotificationType.Error]: '#E64132',
+    [NotificationType.Warning]: '#D08613',
   };
 
-  let removeNotificationTimeout: ReturnType<typeof setTimeout> | undefined;
-
   onMount(() => {
-    removeNotificationTimeout = setTimeout(discard, notificationInfo.timeout);
-    return () => clearTimeout(removeNotificationTimeout);
+    const timeoutId = setTimeout(discard, notification.timeout);
+    return () => clearTimeout(timeoutId);
   });
 
   const discard = () => {
-    notificationController.removeNotificationById(notificationInfo.id);
+    notificationController.removeNotificationById(notification.id);
   };
 
   const handleClick = () => {
-    const action = notificationInfo.action;
-    if (action.type == 'discard') {
+    const action = notification.action;
+    if (action.type === 'discard') {
       discard();
     } else if (action.type == 'link') {
       window.open(action.target);
@@ -83,17 +53,17 @@
 <!-- svelte-ignore a11y-no-static-element-interactions -->
 <div
   transition:fade={{ duration: 250 }}
-  style:background-color={backgroundColor()}
-  style:border={borderStyle()}
-  class="z-[999999] mb-4 min-h-[80px] w-[300px] rounded-2xl p-4 shadow-md hover:cursor-pointer"
+  style:background-color={backgroundColor[notification.type]}
+  style:border-color={borderColor[notification.type]}
+  class="border z-[999999] mb-4 min-h-[80px] w-[300px] rounded-2xl p-4 shadow-md hover:cursor-pointer"
   on:click={handleClick}
   on:keydown={handleClick}
 >
   <div class="flex justify-between">
     <div class="flex place-items-center gap-2">
-      <Icon path={icon} color={primaryColor()} size="20" />
-      <h2 style:color={primaryColor()} class="font-medium" data-testid="title">
-        {notificationInfo.type.toString()}
+      <Icon path={icon} color={primaryColor[notification.type]} size="20" />
+      <h2 style:color={primaryColor[notification.type]} class="font-medium" data-testid="title">
+        {notification.type.toString()}
       </h2>
     </div>
     <button on:click|stopPropagation={discard}>
@@ -102,6 +72,6 @@
   </div>
 
   <p class="whitespace-pre-wrap pl-[28px] pr-[16px] text-sm" data-testid="message">
-    {notificationInfo.message}
+    {notification.message}
   </p>
 </div>
diff --git a/web/src/lib/components/shared-components/notification/notification-list.svelte b/web/src/lib/components/shared-components/notification/notification-list.svelte
index bf8d93d5f2..d94ff5c14d 100644
--- a/web/src/lib/components/shared-components/notification/notification-list.svelte
+++ b/web/src/lib/components/shared-components/notification/notification-list.svelte
@@ -11,9 +11,9 @@
 
 {#if $notificationList.length > 0}
   <section transition:fade={{ duration: 250 }} id="notification-list" class="fixed right-5 top-[80px] z-[99999999]">
-    {#each $notificationList as notificationInfo (notificationInfo.id)}
+    {#each $notificationList as notification (notification.id)}
       <div animate:flip={{ duration: 250, easing: quintOut }}>
-        <NotificationCard {notificationInfo} />
+        <NotificationCard {notification} />
       </div>
     {/each}
   </section>
diff --git a/web/src/lib/components/shared-components/notification/notification.ts b/web/src/lib/components/shared-components/notification/notification.ts
index f1a2140461..52e75bf41f 100644
--- a/web/src/lib/components/shared-components/notification/notification.ts
+++ b/web/src/lib/components/shared-components/notification/notification.ts
@@ -6,57 +6,43 @@ export enum NotificationType {
   Warning = 'Warning',
 }
 
-export class ImmichNotification {
-  id = Date.now() + Math.random();
-  type!: NotificationType;
-  message!: string;
-  action!: NotificationAction;
-  timeout = 3000;
-}
+export type Notification = {
+  id: number;
+  type: NotificationType;
+  message: string;
+  /** The action to take when the notification is clicked */
+  action: NotificationAction;
+  /** Timeout in miliseconds */
+  timeout: number;
+};
 
 type DiscardAction = { type: 'discard' };
 type NoopAction = { type: 'noop' };
 type LinkAction = { type: 'link'; target: string };
 export type NotificationAction = DiscardAction | NoopAction | LinkAction;
 
-export class ImmichNotificationDto {
-  /**
-   * Notification type
-   * @type {NotificationType} [Info, Error]
-   */
-  type: NotificationType = NotificationType.Info;
-
-  /**
-   * Notification message
-   */
-  message = '';
-
-  /**
-   * Timeout in miliseconds
-   */
-  timeout?: number;
-
-  /**
-   * The action to take when the notification is clicked
-   */
-  action?: NotificationAction;
-}
+export type NotificationOptions = Partial<Exclude<Notification, 'id'>> & { message: string };
 
 function createNotificationList() {
-  const notificationList = writable<ImmichNotification[]>([]);
+  const notificationList = writable<Notification[]>([]);
+  let count = 1;
 
-  const show = (notificationInfo: ImmichNotificationDto) => {
-    const newNotification = new ImmichNotification();
-    newNotification.message = notificationInfo.message;
-    newNotification.type = notificationInfo.type;
-    newNotification.timeout = notificationInfo.timeout || 3000;
-    newNotification.action = notificationInfo.action || { type: 'discard' };
+  const show = (options: NotificationOptions) => {
+    notificationList.update((currentList) => {
+      currentList.push({
+        id: count++,
+        type: NotificationType.Info,
+        action: { type: 'discard' },
+        timeout: 3000,
+        ...options,
+      });
 
-    notificationList.update((currentList) => [...currentList, newNotification]);
+      return currentList;
+    });
   };
 
   const removeNotificationById = (id: number) => {
-    notificationList.update((currentList) => currentList.filter((n) => n.id != id));
+    notificationList.update((currentList) => currentList.filter((n) => n.id !== id));
   };
 
   return {