Custom Toast Component in React

Vishnu MohanVishnu Mohan
4 min read

Lets make a basic reusable toast component with three different states to show error, success, and information in a toast with respective custom messages.

Prerequisites include basic knowledge of React hooks "useState" and "useEffect".

Initiate a starter project for your react app, I recommend create-react-app since it will take care of everything needed for this example.

npx create-react-app my-toast

cd my-toast
npm start

The above commands will start your app

The next step is to create our toast component in a file and export it so that we can use it in the App component.

import React, { useEffect } from 'react';
import './style.css';
const Toast = ({ text, type, timer, setter, state }) => {
  return (
      <div className="toast-container">
        <p>{text}</p>
      </div>
  );
};
.toast-container {
  display: flex;
  padding: 1rem 1.6rem;
  width: fit-content;
  border-radius: 8px;
  position: relative;
  box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px;  
}

.toast-indicator {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 8px;
  border-bottom-left-radius: 8px;
  border-top-left-radius: 8px;
}

We have 5 different input props for this component, let's look at each of them.

  1. text - as expected, this prop will carry your custom text to be displayed in the toast.

  2. type - in this example, we have 3 types of toast and this variable will decide which type the toast will be.

  3. timer - you will be using this prop to set the custom time after which the toast will be hidden automatically.

  4. setter - is the function that we will use to set state from inside the toast component, you will get to know it better as we go ahead.

  5. state - this is a boolean variable that decides when to show the toast, we will be injecting it from the parent element.

Let's import the Toast Component and see how it looks

export default function App() {
  const [showToast, setShowToast] = useState(false);
  return (
    <div>
      <Toast
        type="error"
        setter={setShowToast}
        state={showToast}
        text={'Custom Error'}
        timer={3000}
      />
    </div>
  );
}

Here we have set up the state 'showToast' which is to be used inside the Toast component, we are passing both state and the setter function for this state.

Now inside the Toast component, we will use the state to conditionally render the toast.

const Toast = ({ text, type, timer, setter, state }) => {
  return (
    state && (
      <div className="toast-container">
        <p>{text}</p>
      </div>
    )
  );
};
export default Toast;

Now we have the code for mounting the toast conditionally since it is a toast we need to hide it automatically, lets's look at how we can do that

const Toast = ({ text, type, timer, setter, state }) => {
  useEffect(() => {
    setTimeout(() => setter(false), timer || 2000);
  }, [state]);
  return (
    state && (
      <div className="toast-container">
        <p>{text}</p>
      </div>
    )
  );
};
export default Toast;

Since we have added the 'state' prop to the dependency array of the above 'useEffect', the callback function we passed to the hook will be called every time the 'state' prop has changed.

In the call back we are using the setter function that we created in the parent and setting it to false after the timer runs out, this way we have complete control of our toast from our parent.

const Toast = ({ text, type, timer, setter, state }) => {
  useEffect(() => {
    setTimeout(() => setter(false), timer || 2000);
  }, [state]);
  return (
    state && (
      <div className="toast-container">
        <p>{text}</p>
        <div
          className="toast-indicator"
          style={{
            background:
              type == 'error' ? 'red' : type == 'success' ? 'green' : 'blue',
          }}
        ></div>
      </div>
    )
  );
};
export default Toast;

Above we are setting conditional inline style to a class named toast-indicator which will decide the indicator color for each type of toast we need.

Finally, this is how we use our component in App component

export default function App() {
  const [showToast, setShowToast] = useState(false);
  return (
    <>
      <h1>Custom toast</h1>
      <button onClick={() => setShowToast(true)}>show toast</button>

      <Toast
        type="error"
        setter={setShowToast}
        state={showToast}
        text={'Custom Error'}
        timer={3000}
      />
    </>
  );
}

NOTE: In the above example we are using useState hook for state management but in a more sophisticated app where you have many different states and re-usable components you might be using Context API or a state management library like redux, in which case you can use the Toast Component in a single place mostly the first parent in your app and handle the state in a single source of truth from across your app.

Thanks for reading ๐Ÿ‘

0
Subscribe to my newsletter

Read articles from Vishnu Mohan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Vishnu Mohan
Vishnu Mohan

Software Engineer | DevOps & CloudNative