Create toasts from outside of React Components
Published: 2024-04-01 04:25:41

This week, I submitted a [PR](https://github.com/tarasglek/chatcraft.org/pull/539) to [ChatCraft](https://github.com/tarasglek/chatcraft.org) that added an alert function using the Standalone Toasts feature of Chakra UI. I previously used the `useToast` hooks, but I found that the Standalone Toasts are the solution when hooks can't be used. I'd like to share how I implemented it.

## [Standalone Toasts](https://chakra-ui.com/docs/components/toast#standalone-toasts)  

Firstly, let's see the examples from Chakra UI document

```ts
import * as ReactDOM from 'react-dom/client'
import { createStandaloneToast } from '@chakra-ui/react'

const { ToastContainer, toast } = createStandaloneToast()

// render the ToastContainer in your React root
const rootElement = document.getElementById('root')
ReactDOM.createRoot(yourRootElement).render(
  <>
    <App />
    <ToastContainer />
  </>,
)

toast({
  title: 'An error occurred.',
  description: 'Unable to create user account.',
  status: 'error',
  duration: 9000,
  isClosable: true,
})
```

At first glance, I found it a little confusing because it seems like we need to add the ToastContainer somewhere.

```ts
// render the ToastContainer in your React root
const rootElement = document.getElementById('root')
ReactDOM.createRoot(yourRootElement).render(
  <>
    <App />
    <ToastContainer />
  </>,
)

```

Actually, if your project already has `@chakra-ui/react` installed, then we don't need to import it separately. What we need becomes to :
```ts

import { createStandaloneToast } from '@chakra-ui/react'

const { toast } = createStandaloneToast()

toast({
  title: 'An error occurred.',
  description: 'Unable to create user account.',
  status: 'error',
  duration: 9000,
  isClosable: true,
})
``` 

## How I did in the PR


```ts
//utils.ts
/**
 * Only meant to be used outside components or hooks
 * where useAlert cannot be used.
 */
import type { AlertArguments } from "../hooks/use-alert";
export const utilizeAlert = async () => {
  const { createStandaloneToast } = await import("@chakra-ui/react");
  const { toast } = createStandaloneToast();

  const info = ({ id, title, message }: AlertArguments) => {
    toast({
      id,
      title,
      description: message,
      colorScheme: "blue",
      status: "info",
      position: "top",
      isClosable: true,
      duration: 3000,
    });
  };

  const loading = ({ id, title, message }: AlertArguments) => {
    const fallbackId = new Date().toISOString();
    toast({
      id: id ?? fallbackId,
      title,
      description: message,
      colorScheme: "blue",
      status: "loading",
      position: "top",
      isClosable: true,
      duration: null,
    });
    return id ?? fallbackId;
  };

  const closeLoading = (id: string) => {
    toast.close(id);
  };

  return {
    info,
    loading,
    closeLoading,
  };
};
```
As you can see, I wrap different kinds of toasts in the `utilizeAlert` function. Then we can show the alert by:
```ts
import { utilizeAlert } from "./utils";

const nonComponentFunction () {
    const { info , loading, closeLoading } = await utilizeAlert();

    //show info alert
    info({title: "this is info alert"})
    
    //show loading alert
    const alertId = loading({
      title: `Generating image, please wait.`,
    });

    //some stuff

    //close loading alert
    closeLoading(alertId);
}
```

Standalone Toasts are a good design pattern that allows users to create toasts outside of a component. Sometimes it's good to have more options, and I think that's one of the reasons why Chakra UI is so popular. So that's it. I hope you found this helpful. Thanks for reading!