🥁

shadcn/uiでのトラブルシューティング(Toast編)

2023/07/28に公開

はじめに

  • shadcn/ui で Toast を利用した際に、<Toaster> の配置位置によってエラーが出るのでメモとして残します。

https://ui.shadcn.com/docs/components/toast

結論

<body> の配下に <Toaster> を配置すれば問題ありません。

    ...
    <html lang="ja">
      <body className="">
        <main>{children}</main>
        <Toaster />
      </body>
    </html>
    ...

<body> の外に配置するとエラーが出ます。

    ...
    <html lang="ja">
      <body className="">
        <main>{children}</main>
      </body>
      <Toaster />
    </html>
    ...

念のため動作確認します。まずは正しい使い方を確認します。

Next.jsプロジェクトの新規作成

作業するプロジェクトを新規に作成していきます。

長いので、折りたたんでおきます。

新規プロジェクト作成と初期環境構築の手順詳細
$ pnpm create next-app@latest nextjs-shadcnui-toast-error-fix-sample --typescript --eslint --import-alias "@/*" --src-dir --use-pnpm --tailwind --app
$ cd nextjs-shadcnui-toast-error-fix-sample

以下の通り不要な設定を削除し、プロジェクトの初期環境を構築します。

$ mkdir src/styles
$ mv src/app/globals.css src/styles/globals.css
src/styles/globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;
src/app/page.tsx
export default function Home() {
  return (
    <main className="text-lg">
      テストページ
    </main>
  )
}
src/app/layout.tsx
import '@/styles/globals.css'

export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="ja">
      <body className="">{children}</body>
    </html>
  );
}
tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  plugins: [],
};
tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
+    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}

コミットします。

$ pnpm build
$ git add .
$ git commit -m "新規にプロジェクトを作成し, 作業環境を構築"

shadcn-ui の環境を構築

shadcn-ui CLI を実行し、作業環境を構築します。

$ pnpm dlx shadcn-ui@latest init

プロンプトで確認されるので返信していきます。

✔ Would you like to use TypeScript (recommended)? … yes
✔ Which style would you like to use? › Default
✔ Which color would you like to use as base color? › Slate
✔ Where is your global CSS file? … src/styles/globals.css
✔ Would you like to use CSS variables for colors? … yes
✔ Where is your tailwind.config.js located? … tailwind.config.js
✔ Configure the import alias for components: … @/components
✔ Configure the import alias for utils: … @/lib/utils
✔ Are you using React Server Components? … yes
✔ Write configuration to components.json. Proceed? … yes

変更点を確認します。長いので折りたたんでおきます。

変更点の確認

src/styles/globals.cssが変更されました。

src/styles/globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;
+ 
+@layer base {
+  :root {
+    --background: 0 0% 100%;
+    --foreground: 222.2 84% 4.9%;
+ 
+    --muted: 210 40% 96.1%;
+    --muted-foreground: 215.4 16.3% 46.9%;
+ 
+    --popover: 0 0% 100%;
+    --popover-foreground: 222.2 84% 4.9%;
+ 
+    --card: 0 0% 100%;
+    --card-foreground: 222.2 84% 4.9%;
+ 
+    --border: 214.3 31.8% 91.4%;
+    --input: 214.3 31.8% 91.4%;
+ 
+    --primary: 222.2 47.4% 11.2%;
+    --primary-foreground: 210 40% 98%;
+ 
+    --secondary: 210 40% 96.1%;
+    --secondary-foreground: 222.2 47.4% 11.2%;
+ 
+    --accent: 210 40% 96.1%;
+    --accent-foreground: 222.2 47.4% 11.2%;
+ 
+    --destructive: 0 84.2% 60.2%;
+    --destructive-foreground: 210 40% 98%;
+ 
+    --ring: 215 20.2% 65.1%;
+ 
+    --radius: 0.5rem;
+  }
+ 
+  .dark {
+    --background: 222.2 84% 4.9%;
+    --foreground: 210 40% 98%;
+ 
+    --muted: 217.2 32.6% 17.5%;
+    --muted-foreground: 215 20.2% 65.1%;
+ 
+    --popover: 222.2 84% 4.9%;
+    --popover-foreground: 210 40% 98%;
+ 
+    --card: 222.2 84% 4.9%;
+    --card-foreground: 210 40% 98%;
+ 
+    --border: 217.2 32.6% 17.5%;
+    --input: 217.2 32.6% 17.5%;
+ 
+    --primary: 210 40% 98%;
+    --primary-foreground: 222.2 47.4% 11.2%;
+ 
+    --secondary: 217.2 32.6% 17.5%;
+    --secondary-foreground: 210 40% 98%;
+ 
+    --accent: 217.2 32.6% 17.5%;
+    --accent-foreground: 210 40% 98%;
+ 
+    --destructive: 0 62.8% 30.6%;
+    --destructive-foreground: 0 85.7% 97.3%;
+ 
+    --ring: 217.2 32.6% 17.5%;
+  }
+}
+ 
+@layer base {
+  * {
+    @apply border-border;
+  }
+  body {
+    @apply bg-background text-foreground;
+  }
+}

src/lib/utils.tsが新規作成されました。

src/lib/utils.ts
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
 
export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

components.jsonが新規作成されました。

src/components.json
{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "default",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "config": "tailwind.config.js",
    "css": "src/styles/globals.css",
    "baseColor": "slate",
    "cssVariables": true
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils"
  }
}

package.jsonにパッケージが追加されています。

package.json
{
  "name": "nextjs-shadcnui-contact-sample",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@types/node": "20.4.4",
    "@types/react": "18.2.15",
    "@types/react-dom": "18.2.7",
    "autoprefixer": "10.4.14",
+    "class-variance-authority": "^0.7.0",
+    "clsx": "^2.0.0",
    "eslint": "8.45.0",
    "eslint-config-next": "13.4.12",
+    "lucide-react": "^0.263.0",
    "next": "13.4.12",
    "postcss": "8.4.27",
    "react": "18.2.0",
    "react-dom": "18.2.0",
+    "tailwind-merge": "^1.14.0",
    "tailwindcss": "3.3.3",
+    "tailwindcss-animate": "^1.0.6",
    "typescript": "5.1.6"
  }
}

コミットします。

$ pnpm build
$ git add .
$ git commit -m "shadcn/uiの環境を構築"

Toastを追加

Toast を追加します。

https://ui.shadcn.com/docs/components/toast

パッケージをインストールし、実装します。

$ pnpm dlx shadcn-ui@latest add toast
$ pnpm dlx shadcn-ui@latest add button

ボタンをクリックしたら Toast が表示されるようにしたいので、自前でコンポーネントを作成します。

$ touch src/components/button.tsx
src/components/button.tsx
"use client"
 
import { Button } from "@/components/ui/button"
import { useToast } from "@/components/ui/use-toast"
 
export function CustomButton() {
  const { toast } = useToast()
 
  return (
    <Button
      variant="outline"
      onClick={() => {
        toast({
          description: "Your message has been sent.",
        })
      }}
    >
      Show Toast
    </Button>
  )
}

<Toaster> を配置します。

src/app/layout.tsx
import "@/styles/globals.css";
+import { Toaster } from "@/components/ui/toaster";

export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="ja">
      <body className="">
+       <main>{children}</main>
+       <Toaster />
      </body>
    </html>
  );
}

自前で作成下コンポーネントを配置します。

src/app/page.tsx
export default function Home() {
  return (
    <main className="text-lg">
-      テストページ
+      <CustomButton />
    </main>
  )
}

ローカルサーバで動作確認をします。

$ pnpm dev

ボタンをクリックすると、右下に Toast が表示されました。

コミットします。

$ pnpm build
$ git add .
$ git commit -m "Toastを実装"

間違いそうな部分

続いて間違った使い方を確認します。

<Toaster /> の位置が <body> の中にないと、エラーが発生します。

src/app/layout.tsx
import "@/styles/globals.css";
import { Toaster } from "@/components/ui/toaster";

export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="ja">
      <body className="">
        <main>{children}</main>
-        <Toaster />
      </body>
+      <Toaster />
    </html>
  );
}

ローカルサーバで実行すると以下のエラーが発生します。

まとめ

  • shadcn/uiToast を利用する際に、<Toaster> の位置が <body> の中にないとエラーが発生します。
  • 作業したプロジェクトは以下にあります。

https://github.com/hayato94087/nextjs-shadcnui-toast-error-fix-sample

Discussion