How to Create a Notification/Toast using React and Tailwind

Overview

One of the most important components to communicate something more effectively with the user is notifications. Obviously there are several types of notifications, some can be alerts others can be to communicate a certain operation. And their anatomy differs a lot, for example alerts, toasts and snackbars usually contain your information in a single line, with or without icons.

But all genres have some aspects in common, such as:

  • positioning - can be placed in different positions on the screen;
  • animations - taking their placement into account, they all end up following exactly the same patterns;
  • actions - they all have only one action, whether it's close, dismiss or cancel.

One of the things we have to keep in mind is that notifications tend to be shown above all platform content, so that it is easily noticeable to the user. However, they cannot block other action elements, such as buttons.

Today's example

In today's example we will create a simple notification using two of my favorite libraries. For component styling we will use Tailwind and to help us create our notification we will use the React Hot Toast library.

Our component will consist of four elements, the icon, the title, the text and the action (dismiss). While all the styling and animations will be done with Tailwind, all the hard work of creating a notification will be done entirely by React Hot Toast.

From the code in this article I hope that you will be able to create several different notifications/toasts, but that you will have a similar result to this one:

Let's code

First let's install the following dependencies:

npm install classnames react-icons react-hot-toast

Now in our App.jsx we will import our dependencies:

// @src/App.jsx

import React from "react";
import classNames from "classnames";
import toast, { Toaster } from "react-hot-toast";
import { MdOutlineClose } from "react-icons/md";
import { HiLightningBolt } from "react-icons/hi";

// ...

Then let's create the styles that will be used in our App.jsx:

/* @src/App.module.css */

.notificationWrapper {
  @apply flex flex-row items-center justify-between w-96 bg-gray-900 px-4 py-6 text-white shadow-2xl hover:shadow-none transform-gpu translate-y-0 hover:translate-y-1 rounded-xl relative transition-all duration-500 ease-in-out;
}

.iconWrapper {
  @apply text-xl;
}

.contentWrapper {
  @apply flex flex-col items-start justify-center ml-4 cursor-default;
}

.contentWrapper h1 {
  @apply text-base text-gray-200 font-semibold leading-none tracking-wider;
}

.contentWrapper p {
  @apply text-sm text-gray-400 mt-2 leading-relaxed tracking-wider;
}

.closeIcon {
  @apply absolute top-2 right-2 cursor-pointer text-lg;
}

Now we can continue to work on our App.jsx. First we'll import the styles we just created and then we'll start working on our notification component.

// @src/App.jsx

import React from "react";
import classNames from "classnames";
import toast, { Toaster } from "react-hot-toast";
import { MdOutlineClose } from "react-icons/md";
import { HiLightningBolt } from "react-icons/hi";

import styles from "./App.module.css";

const notify = () =>
  toast.custom(
    (t) => (
      <div
        className={classNames([
          styles.notificationWrapper,
          t.visible ? "top-0" : "-top-96",
        ])}
      >
        <div className={styles.iconWrapper}>
          <HiLightningBolt />
        </div>
        <div className={styles.contentWrapper}>
          <h1>New version available</h1>
          <p>
            An improved version of VESSEL is now available, refresh to update.
          </p>
        </div>
        <div className={styles.closeIcon} onClick={() => toast.dismiss(t.id)}>
          <MdOutlineClose />
        </div>
      </div>
    ),
    { id: "unique-notification", position: "top-center" }
  );

// ...

All that's left is to create our App component, which will only contain a button to show the notification and the Toaster component (responsible for rendering all toasts).

// @src/App.jsx

import React from "react";
import classNames from "classnames";
import toast, { Toaster } from "react-hot-toast";
import { MdOutlineClose } from "react-icons/md";
import { HiLightningBolt } from "react-icons/hi";

import styles from "./App.module.css";

const notify = () =>
  toast.custom(
    (t) => (
      <div
        className={classNames([
          styles.notificationWrapper,
          t.visible ? "top-0" : "-top-96",
        ])}
      >
        <div className={styles.iconWrapper}>
          <HiLightningBolt />
        </div>
        <div className={styles.contentWrapper}>
          <h1>New version available</h1>
          <p>
            An improved version of VESSEL is now available, refresh to update.
          </p>
        </div>
        <div className={styles.closeIcon} onClick={() => toast.dismiss(t.id)}>
          <MdOutlineClose />
        </div>
      </div>
    ),
    { id: "unique-notification", position: "top-center" }
  );

const App = () => {
  return (
    <div>
      <button onClick={notify}>Notify</button>
      <Toaster />
    </div>
  );
};

export default App;

As you may have noticed in our notification component, we assign an id so that only one toast is shown in the gift, if you remove it you will notice that the behavior will change. Starting to be rendered several notifications in the dom.

Conclusion

As always, I hope you found it interesting. If you noticed any errors in this article, please mention them in the comments. đŸ§‘đŸ»â€đŸ’»

Hope you have a great day! 😈

19