🍞

Next.js App routerでのReact-Toastifyの設定方法

に公開

(元記事はdev.toに投稿したこちらの記事です)

Toast UIのライブラリを入れるならとりあえずReact-Toastifyという方は多いのではないでしょうか。設定や使い方がシンプルで簡単なので自分も普段からとてもお世話になっています。
ただNext.jsのApp routerを使う時はちょっとしたワークアラウンドが必要になりますので、この記事で紹介したいと思います。

ToastContainerをラップするクライアントコンポーネントを作成

ToastContainerはクライアントコンポーネント内に配置されなければ動作しません。
Pages routerであればApp.tsxなどにそのまま配置しますが、App routerの場合ルートコンポーネントはapp/layout.tsxであり、このコンポーネントはサーバーコンポーネントです。
なので一旦クライアントコンポーネントでラップしてやる必要があります。

ToastProviderというクライアントコンポーネントを作成し、その中にToastContainerを配置します。
(ちなみにlayout.tsxをクライアントコンポーネント化する方法もありそうですが...それは試せていません。試した方がいらっしゃればコメントなどで教えていただけると嬉しいです!)

"use client";

import "react-toastify/dist/ReactToastify.css";
import "../../app/globals.css";
import { ToastContainer } from "react-toastify";

interface ToastProviderProps {
  children: React.ReactNode;
}

export default function ToastProvider({ children }: ToastProviderProps) {

  return (
    <>
      {children}
      <ToastContainer />
    </>
  );
}

ちなみにベースとなる設定やスタイルはこのコンポーネントで定義できます。以下はTailwind CSSを使用した例です。

"use client";

import "react-toastify/dist/ReactToastify.css";
import "../../app/globals.css";
import { ToastContainer } from "react-toastify";

interface ToastProviderProps {
  children: React.ReactNode;
}

export default function ToastProvider({ children }: ToastProviderProps) {
  const contextClass = {
    success: "bg-blue-600",
    error: "bg-red-600",
    info: "bg-gray-600",
    warning: "bg-orange-400",
    default: "bg-indigo-600",
    dark: "bg-white-600 font-gray-300",
  };

  return (
    <>
      {children}
      <ToastContainer
        toastClassName={(context) =>
          contextClass[context?.type || "default"] +
          " relative flex p-1 min-h-10 rounded-md justify-between overflow-hidden cursor-pointer"
        }
        bodyClassName={() => "text-sm font-white font-med block p-3"}
        position="bottom-left"
        autoClose={3000}
      />
    </>
  );
}

ToastProviderをapp/layout.tsxで使用する

あとはさきほど作成したToastProviderをただルートレイアウトコンポーネントに配置すれば設定完了です。

import type { Metadata } from "next"
import { Inter } from "next/font/google"
import Favicon from "@/app/icon.ico";
import "./globals.css"
import ToastProvider from "@/lib/react-toastify/ToastProvider"

const inter = Inter({ subsets: ["latin"] })

export const metadata: Metadata = {
  title: "Toast app",
  description: "Toast app is just a sample app for demonstrating react-toastify library.",
  icons: [{ rel: 'icon', url: Favicon.src }]
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <ToastProvider>
          {children}
        </ToastProvider>
      </body>
    </html>
  )
}

これで完了です!
あとは公式ドキュメントにある通り

toast('🦄 Wow so easy!', {
  position: "top-right",
  autoClose: 5000,
  hideProgressBar: false,
  closeOnClick: true,
  pauseOnHover: true,
  draggable: true,
  progress: undefined,
  theme: "light",
  transition: Bounce,
});

のようにすればどこでもトーストを呼び出すことができます!

Discussion