Toasts can be used to show contextual messages to the user that are not part of the page. A good example is showing an error message if a request to the backend fails. Another would be showing a success message after the user changes their password - as there is no direct confirmation via the page content.

Firstly lets create a ToastContext that we can use throughout the app to add toasts as required by adding the following to frontend/src/ToastContext.tsx,

import { Color } from "@material-ui/lab/Alert";
import React from "react";

export interface IToast {
  category?: Color;
  message: string;

interface IToastContext {
  addToast: (toast: IToast) => void;
  setToasts: React.Dispatch<React.SetStateAction<IToast[]>>;
  toasts: IToast[];

export const ToastContext = React.createContext<IToastContext>({
  addToast: () => {},
  setToasts: () => {},
  toasts: [],

interface IProps {
  children?: React.ReactNode;

export const ToastContextProvider = ({ children }: IProps) => {
  const [toasts, setToasts] = React.useState<IToast[]>([]);

  const addToast = (toast: IToast) => {
    setToasts((toasts) => [...toasts, toast]);

  return (
    <ToastContext.Provider value={{ addToast, setToasts, toasts }}>

We'll use the Material-UI snackbar to display toasts, and the Material-UI Alert to style the snack depending on the category (error, success, etc). The Alert is currently part of the Material-UI lab that can be installed via npm,

Run this command in frontend/

npm install --save @material-ui/lab

We can then create a Toasts component to display them. Note that only one toast should be displayed at any point in time, so a useEffect is setup to take and display a toast whenever there are toasts to display and there isn't an open one. The following should be added to frontend/src/components/Toasts.tsx,

import Snackbar from "@material-ui/core/Snackbar";
import Alert from "@material-ui/lab/Alert";
import React, { useEffect } from "react";

import { ToastContext, IToast } from "src/ToastContext";

const Toasts = () => {
  const { toasts, setToasts } = React.useContext(ToastContext);
  const [open, setOpen] = React.useState(false);
  const [currentToast, setCurrentToast] = React.useState<IToast | undefined>();

  useEffect(() => {
    if (!open && toasts.length) {
      setToasts((prev) => prev.slice(1));
  }, [open, setCurrentToast, setOpen, setToasts, toasts]);

  const onClose = (event?: React.SyntheticEvent, reason?: string) => {
    if (reason !== "clickaway") {

  return (
        horizontal: "center",
        vertical: "top",
      onExited={() => setCurrentToast(undefined)}

export default Toasts;

Finally we can add the following to src/App.tsx to enable toasts in the app,

import Toasts from "src/components/Toasts";
import { ToastContextProvider } from "src/ToastContext";

const App = () => {
  return (
      <Toasts />