diff --git a/src/lib/components/Toast.svelte b/src/lib/components/Toast.svelte
new file mode 100644
index 0000000..9d6a9ed
--- /dev/null
+++ b/src/lib/components/Toast.svelte
@@ -0,0 +1,66 @@
+
+
+
+ {#if type === "success"}
+
+ {:else if type === "error"}
+
+ {:else}
+
+ {/if}
+
+
+
+
+
+ {#if dismissible}
+
+ {/if}
+
+
+
diff --git a/src/lib/components/Toasts.svelte b/src/lib/components/Toasts.svelte
new file mode 100644
index 0000000..379c894
--- /dev/null
+++ b/src/lib/components/Toasts.svelte
@@ -0,0 +1,32 @@
+
+
+{#if $toasts}
+
+ {#each $toasts as toast (toast.id)}
+ dismissToast(toast.id)}>{toast.message}
+ {/each}
+
+{/if}
+
+
diff --git a/src/lib/components/store.ts b/src/lib/components/store.ts
new file mode 100644
index 0000000..07ad73c
--- /dev/null
+++ b/src/lib/components/store.ts
@@ -0,0 +1,70 @@
+import { writable } from "svelte/store";
+
+interface ToastNotification {
+ id: number;
+ type: "info" | "success" | "error" | "warning";
+ dismissible: boolean;
+ timeout: number;
+ message: string;
+}
+
+export const toasts = writable([]);
+
+interface InternalToast {
+ type: "info" | "success" | "error" | "warning";
+ dismissible: boolean;
+ timeout: number;
+ message: string;
+}
+
+const addToast = (toast: InternalToast) => {
+ // Create a unique ID so we can easily find/remove it
+ // if it is dismissible/has a timeout.
+ const id = Math.floor(Math.random() * 10000);
+
+ // Setup some sensible defaults for a toast.
+ const defaults = {
+ id,
+ type: "info",
+ dismissible: true,
+ timeout: 3000,
+ };
+
+ // Push the toast to the top of the list of toasts
+ toasts.update((all) => [{ ...defaults, ...toast }, ...all]);
+
+ // If toast is dismissible, dismiss it after "timeout" amount of time.
+ if (toast.timeout) setTimeout(() => dismissToast(id), toast.timeout);
+};
+
+export const dismissToast = (id: number) => {
+ toasts.update((all) => all.filter((t) => t.id !== id));
+};
+
+export const addSuccessToast = (message: string, dismissible: boolean = false, timeout: number = 1000) => {
+ addToast({
+ type: "success",
+ dismissible,
+ timeout,
+ message,
+ })
+}
+
+export const addErrorToast = (message: string, dismissible: boolean = false, timeout: number = 1000) => {
+ addToast({
+ type: "error",
+ dismissible,
+ timeout,
+ message,
+ })
+}
+
+export const addInfoToast = (message: string, dismissible: boolean = false, timeout: number = 1000) => {
+ addToast({
+ type: "error",
+ dismissible,
+ timeout,
+ message,
+ })
+}
+
diff --git a/src/lib/icons/CloseIcon.svelte b/src/lib/icons/CloseIcon.svelte
new file mode 100644
index 0000000..2911de3
--- /dev/null
+++ b/src/lib/icons/CloseIcon.svelte
@@ -0,0 +1,18 @@
+
+
+
diff --git a/src/lib/icons/ErrorIcon.svelte b/src/lib/icons/ErrorIcon.svelte
new file mode 100644
index 0000000..059d916
--- /dev/null
+++ b/src/lib/icons/ErrorIcon.svelte
@@ -0,0 +1,19 @@
+
+
+
diff --git a/src/lib/icons/InfoIcon.svelte b/src/lib/icons/InfoIcon.svelte
new file mode 100644
index 0000000..d39711b
--- /dev/null
+++ b/src/lib/icons/InfoIcon.svelte
@@ -0,0 +1,18 @@
+
+
+
diff --git a/src/lib/icons/SuccessIcon.svelte b/src/lib/icons/SuccessIcon.svelte
new file mode 100644
index 0000000..18a7b93
--- /dev/null
+++ b/src/lib/icons/SuccessIcon.svelte
@@ -0,0 +1,18 @@
+
+
+