【Vite × React × TypeScript】Chakra UI v3でトーストの閉じるボタンを表示・機能させる方法

# はじめに

お疲れ様です。りつです。

今回は、タイトルの通りChakra UI v3のトーストにまつわるtipsをご紹介します。

# 問題

Chakra UIの v1、v2まではトーストに`isClosable`というオプションがあり、このオプションが`true`だとトーストの閉じるボタンが表示されていたようです。

しかし、Chakra UI v3だとこのオプション自体がエラーになってしまい使用できませんでした。

どうにかして表示できないものかといろいろ試してみたところ、以下のように表示させることができたので、その方法をご紹介します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3911933/a7c1f102-8016-18da-7689-12a7418dc1ec.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3911933/0783c1ba-5a8a-38f1-165c-7b67a0e8bf4d.png)

# 解決方法

トーストの閉じるボタンを機能させるために以下を実施します。
1. トーストの閉じる機能の有効化
2. トーストの閉じるボタンを見やすく調整

## 1. トーストの閉じる機能の有効化

まず、トーストの閉じる機能を有効化する必要があります。
こちらは、`toaster.create`に指定するオプションで、`meta.closable`を設定します。

具体的には、以下のソースコードをご参照ください。

```diff_typescript:src/hooks/useMessage.ts
import { toaster } from "@/components/ui/toaster"
import { useCallback } from "react"

type Props = {
  title: string;
  type: "info" | "warning" | "success" | "error";
}

export const useMessage = () => {
  const showMessage = useCallback((prpps: Props) => {
    const { title, type } = prpps;
    toaster.create({
      title,
      type,
      duration: 2000,
+     meta: { closable: true } // トーストを閉じる処理を有効化
    })
  }, []);
  return { showMessage }
}
```

<details><summary>【 参考 】showMessageの呼び出し元</summary>

```typescript:src/hooks/useAuth.ts
import { useCallback, useState } from "react";
import { useNavigate } from "react-router";
import axios from "axios";
import { User } from "@/types/api/user";
import { useMessage } from "./useMessage";

export const useAuth = () => {
  const navigate = useNavigate();
  const { showMessage } = useMessage();

  const [loading, setLoading] = useState(false);

  const login = useCallback((id: string) => {
    setLoading(true);

    axios.get<User>(`https://jsonplaceholder.typicode.com/users/${id}`)
    .then((res) => {
      if (res.data) {
        showMessage({ title: 'ログインしました', type: 'success' });
        navigate('/home');
      } else {
        showMessage({ title: 'ユーザーが見つかりません', type: 'error' });
      }
    })
    .catch(() => showMessage({ title: 'ログインできません', type: 'error' }))
    .finally(() => setLoading(false));
  }, [navigate, showMessage]);

  return { login, loading };
};
```

</details>

<details><summary>【 参考 】Toasterコンポーネントの呼び出し元</summary>

```react:src/App.tsx
import { Router } from '@/router/Router'
import { Toaster } from "@/components/ui/toaster"

function App() {
  return (
    <>
      <Toaster />
      <Router />
    </>
  )
}

export default App
```

</details>


## 2. トーストの閉じるボタンを見やすく調整

続いて、トーストの閉じるボタンのスタイルを調整します。

デフォルトだとなぜか閉じるボタンがほぼ目視できない上に、ものすごく小さく表示されておりクリックしずらく、視認性が悪いです。

- 【 参考 】トーストの閉じる機能ON時の表示（閉じるボタンが表示されていないように見えますが、極小サイズで存在はしており、うまくクリックすると閉じることが可能です）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3911933/bc0ece8b-6fee-e5fe-2e2b-0b01f8a4df98.png)

そのため、`src/components/ui/toaster.tsx`の`<Toast.CloseTrigger/>`コンポーネントにスタイルを設定して、見やすく調整していきます。

:::note info
上記ファイルは以下のコマンドを実行すると自動生成されます。
:::

```terminal
npx @chakra-ui/cli snippet add toaster
```

https://www.chakra-ui.com/docs/components/toast#setup

> If you don't already have the snippet, run the following command to add the `toaster` snippet

```diff_tsx:src/components/ui/toaster.tsx
"use client"

import {
  Toaster as ChakraToaster,
  Portal,
  Spinner,
  Stack,
  Toast,
  createToaster,
} from "@chakra-ui/react"

export const toaster = createToaster({
- placement: "bottom-end",
+ placement: "top", // 表示位置をページ上部に指定（ここの修正は任意でOKです）
  pauseOnPageIdle: true
})

export const Toaster = () => {
  return (
    <Portal>
      <ChakraToaster toaster={toaster} insetInline={{ mdDown: "4" }}>
        {(toast) => (
          <Toast.Root width={{ md: "sm" }}>
            {toast.type === "loading" ? (
              <Spinner size="sm" color="blue.solid" />
            ) : (
              <Toast.Indicator />
            )}
            <Stack gap="1" flex="1" maxWidth="100%">
              {toast.title && <Toast.Title>{toast.title}</Toast.Title>}
              {toast.description && (
                <Toast.Description>{toast.description}</Toast.Description>
              )}
            </Stack>
            {toast.action && (
              <Toast.ActionTrigger>{toast.action.label}</Toast.ActionTrigger>
            )}
-           {toast.meta?.closable && <Toast.CloseTrigger/>}
+           {toast.meta?.closable &&
+             <Toast.CloseTrigger
+               style={{width:'25px', height:'25px', color: 'white'}}
+               _hover={{ cursor: 'pointer' }}/>
+           } {/* Toast.CloseTriggerに対してスタイルを設定 */}
          </Toast.Root>
        )}
      </ChakraToaster>
    </Portal>
  )
}
```

以上の手順で、トーストの閉じるボタンを表示・機能させることができます。

# おわりに

今回は調べても情報がなかなか得られませんでした。

ソースコードを辿って推測しながら動かしてみた結果、トーストの閉じるボタンを機能させることができました。

もっと適切な方法があるかもしれませんので、ご存じの方がいたらぜひ教えてください。

# 参考

https://www.chakra-ui.com/docs/components/toast#setup