How to Create a Toast Message UI with JavaScript and CSS

By
coding
javascript
css
ui/ux
Open in Demo.js

A toast message is a small, self-dismissing pop-up notification used to provide quick feedback or brief information about an operation. It typically appears at the bottom of the screen and disappears automatically without interrupting the user's workflow. While these are very common in mobile devices, we will explore how to create our own toast message UI for web applications.

Look at the following example. Click the button to generate a toast message and see how it appears. And try clicking frequently to see how multiple toast messages will behave.

First, we need an HTML element to maintain all the toast messages. Since toast messages are dynamically created while their execution time, we are only going to need this element initially.

<!-- toast messages container -->
<div class="toast-tray"></div>

This tray element should be placed at the bottom of the screen, mostly on the left side. Therefore, we can have some CSS as follows to make that happen. And the flex rules will arrange toast messages in a vertical direction, one on top of another.

/* toast container */
.toast-tray {
  /* placements */
  position: fixed;
  /* attach on bottom left of screen */
  left: 0px;
  bottom: 0px;
  /* placement and spacing for items */
  display: flex;
  flex-direction: column;
  padding: 0px 10px;
}

Now we need two CSS keyframe animations to handle the toast message animations. One for fade in and one for fade out. Fade in will start from no opacity, and will be placed below than usual. So when the fade-in animation ends, it will have to have full opacity, correct placement, while acting as a fade-in bottom-to-top direction animation. The final 10px of margin is to create the gap between each toast message. We are not creating that gap on the tray element, so we can decrease it individually when they disappear.

/* fade in animation */
@keyframes toast-fade-in {
  from {
    opacity: 0;
    margin-bottom: -20px;
  }

  to {
    opacity: 1;
    margin-bottom: 10px;
  }
}

So in the fade-out disappearance animation, we reduce the opacity back to 0 and decrease the height to 0px. Also, we need to clear the gap of clearing items by reducing the margin-bottom amount. This way we can dismiss the toast message while reducing its full height to zero.

/* fade out animation */
@keyframes toast-fade-out {
  from {
    opacity: 1;
    height: 48px;
    margin-bottom: 10px;
  }

  to {
    opacity: 0;
    height: 0px;
    margin-bottom: 0px;
  }
}

We can have some CSS for the toast message. We should have the fade-in animation initially on the toad message element. An important thing is to set the iteration count to 1 and fill mode to forwards so the animation only runs once and stops at the last state without resetting. In the same way, we can have the fade-out animation, where we can close it later by setting an attribute on that, like data-closed. We need to handle that part in JavaScript. Also, remember to hide the overflowing content, so reducing height will work correctly, as in the above animation rules.

/* toast message */
.toast-message {
  /* spacing */
  height: 48px;
  padding: 0px 20px;
  overflow: hidden;
  /* text styles */
  color: #fff;
  line-height: 48px;
  /* initial appearance */
  margin-bottom: -20px;
  opacity: 0;
  /* decorations */
  background-color: #555;
  border-radius: 10px;
  user-select: none;
  /* animation configurations */
  animation-name: toast-fade-in;
  animation-duration: 0.5s;
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
}

/* closed toast message */
.toast-message[data-closed] {
  animation-name: toast-fade-out;
  animation-duration: 0.3s;
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
  animation-timing-function: linear;
}

Finally, we can write the JavaScript code and have a global function to create toast messages. We simply create a div element, and the class name and message content on it. To place the newest messages on top of previous messages, we can use the prepend method on the tray element. The fade-in animation will work automatically because of the CSS we had. Then we wait for a while so the user will have some time to read the message. Later, we will use the data-closed attribute, and the fade-out animation will work. Since the fade-out animation duration we set is 300ms, we wait that amount of time and remove the element from the tray. For these time delays, we can simply use the setTimeout method.

// get toast tray element
const toastTray = document.querySelector(".toast-tray")

const createToast = message => {
  // create toast element
  const toastItem = document.createElement("div")
  // set class name
  toastItem.className = "toast-message"
  // set message content
  toastItem.innerHTML = message
  // append on tray element
  toastTray.prepend(toastItem)
  // toast appearance delay
  setTimeout(() => {
    // set closed attribute
    toastItem.setAttribute("data-closed", true)
    // remove after fade out animations
    setTimeout(() => toastItem.remove(), 300)
  }, 2500)
}

// example usage
createToast("This is an example toast message")