🚀 Svelte Quick Tip: Creating a toast notification system

danawoodman

Dana Woodman

Posted on March 18, 2021

🚀 Svelte Quick Tip: Creating a toast notification system

Toast you say? 🍞

A common UI design pattern is to use "toasts" or small UI notifications that alert the user of something happening in realtime (e.g. a form submission error, a new message or friend request, etc).

In this article, we will be building a simple toast system in Svelte, kinda like this:

toasts

Impatient? See the REPL here


Create a Svelte store for out toast notifications

Let's start out by creating a simple Svelte store for our toast system. The store will just contain an array that we will update when a new toast is created or "dismissed":

import { writable } from 'svelte/store'

export const toasts = writable([])

export const dismissToast = (id) => {
  toasts.update((all) => all.filter((t) => t.id !== id))
}

export const addToast = (toast) => {
  // 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
  const t = { ...defaults, ...toast }
  toasts.update((all) => [t, ...all])

  // If toast is dismissible, dismiss it after "timeout" amount of time.
  if (t.timeout) setTimeout(() => dismissToast(id), t.timeout)
}
Enter fullscreen mode Exit fullscreen mode

Overall this should be pretty simple, we have a two methods, one for adding a toast and the other for removing. If the toast has a timeout field, we set a timeout to remove the toast. We set some default values for all toasts and we give a toast an id to make it easier to add/remove and for Svelte's {#each} tag to index it better.


Create the toasts parent component

<script lang="ts">
  import Toast from './Toast.svelte'

  import { dismissToast, toasts } from './store'
</script>

{#if $toasts}
  <section>
    {#each $toasts as toast (toast.id)}
      <Toast
        type={toast.type}
        dismissible={toast.dismissible}
        on:dismiss={() => dismissToast(toast.id)}>{toast.message}</Toast>
    {/each}
  </section>
{/if}

<style lang="postcss">
  section {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    width: 100%;
    display: flex;
    margin-top: 1rem;
    justify-content: center;
    flex-direction: column;
    z-index: 1000;
  }
</style>
Enter fullscreen mode Exit fullscreen mode

Create the toast component

Next, we're going to create a Toast.svelte component with different states: success, error, and info, like so:

toasts

<script>
  import { createEventDispatcher } from 'svelte'
  import { fade } from 'svelte/transition'
  import SuccessIcon from './SuccessIcon.svelte'
  import ErrorIcon from './ErrorIcon.svelte'
  import InfoIcon from './InfoIcon.svelte'
  import CloseIcon from './CloseIcon.svelte'

  const dispatch = createEventDispatcher()

  export let type = 'error'
  export let dismissible = true
</script>

<article class={type} role="alert" transition:fade>
  {#if type === 'success'}
    <SuccessIcon width="1.1em" />
  {:else if type === 'error'}
    <ErrorIcon width="1.1em" />
  {:else}
    <InfoIcon width="1.1em" />
  {/if}

  <div class="text">
    <slot />
  </div>

  {#if dismissible}
    <button class="close" on:click={() => dispatch('dismiss')}>
      <CloseIcon width="0.8em" />
    </button>
  {/if}
</article>

<style lang="postcss">
  article {
    color: white;
    padding: 0.75rem 1.5rem;
    border-radius: 0.2rem;
    display: flex;
    align-items: center;
    margin: 0 auto 0.5rem auto;
    width: 20rem;
  }
  .error {
    background: IndianRed;
  }
  .success {
    background: MediumSeaGreen;
  }
  .info {
    background: SkyBlue;
  }
  .text {
    margin-left: 1rem;
  }
  button {
    color: white;
    background: transparent;
    border: 0 none;
    padding: 0;
    margin: 0 0 0 auto;
    line-height: 1;
    font-size: 1rem;
  }
</style>
Enter fullscreen mode Exit fullscreen mode

Hopefully this component is pretty straight forward; it's just some styling for the toast, some conditions for if it is "dismissible" and come icon components (which are just SVGs).


Creating toast notifications

You can now create toast notifications anywhere in your Svelte app (in your JS files or your .svelte files):

import { addToast } from "./store";

addToast({
  message: "Hello, World!",
  type: "success",
  dismissible: true,
  timeout: 3000,
});
Enter fullscreen mode Exit fullscreen mode

You can then use your <Toasts /> component somewhere in your layout component (e.g. App.svelte or _layout.svelte, etc).


Wrapping up 🌯

That's it folks, hopefully you learning something today!

See the full toast system in the Svelte REPL here.

Thanks for reading!


Thanks for reading! Consider giving this post a ❤️, 🦄 or 🔖 to bookmark it for later. 💕

Have other tips, ideas, feedback or corrections? Let me know in the comments! 🙋‍♂️

Don't forget to follow me on Dev.to (danawoodman), Twitter (@danawoodman) and/or Github (danawoodman)!

Photo by Joshua Aragon on Unsplash

💖 💪 🙅 🚩
danawoodman
Dana Woodman

Posted on March 18, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related