Next UIでモーダルを追加する

モーダルを追加したい
このブログのカバー画像はAIで作ってるんですけど、カバー画像だけをギャラリー形式で見れるページを作ることにしました。
グリッド形式で画像を見せるところまではTailwindのギャラリーのサンプルを真似してできたのだけど、画像をクリックして拡大して見せるのは別途モーダルを作らないといけないらしい。
フロント音痴のワイ、DOMいじったりスタイリングするだけでヒーヒー言ってるのにモーダル作るなんて無理です。(大昔業務で作ったこともあったけど大変だった記憶)
ライブラリを探してみると、Next UIという、Next.js専用でTailwindとも統合してるいい感じのライブラリがあったので、モーダルだけNext UIを使ってみることにしました。
Next UIのパッケージをインストール
最初にnpm install @nextui-org/react
でパッケージを追加しようとすると、以下のエラーでつっかかってしまいました。
npm install @nextui-org/react
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR!
npm ERR! While resolving: framer-motion@11.15.0
npm ERR! Found: react@19.0.0-rc-02c0e824-20241028
npm ERR! node_modules/react
npm ERR! peer react@">=18" from @nextui-org/form@2.1.7
npm ERR! node_modules/@nextui-org/react/node_modules/@nextui-org/form
npm ERR! @nextui-org/form@"2.1.7" from @nextui-org/react@2.6.10
npm ERR! node_modules/@nextui-org/react
npm ERR! @nextui-org/react@"*" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peerOptional react@"^18.0.0 || ^19.0.0" from framer-motion@11.15.0
npm ERR! node_modules/framer-motion
npm ERR! peer framer-motion@">=11.5.6 || >=12.0.0-alpha.1" from @nextui-org/react@2.6.10
npm ERR! node_modules/@nextui-org/react
npm ERR! @nextui-org/react@"*" from the root project
npm ERR! peer framer-motion@">=11.5.6 || >=12.0.0-alpha.1" from @nextui-org/accordion@2.2.6
npm ERR! node_modules/@nextui-org/accordion
npm ERR! @nextui-org/accordion@"2.2.6" from @nextui-org/react@2.6.10
npm ERR! node_modules/@nextui-org/react
npm ERR! @nextui-org/react@"*" from the root project
npm ERR! 17 more (@nextui-org/system, @nextui-org/autocomplete, ...)
npm ERR!
npm ERR! Conflicting peer dependency: react@19.0.0
npm ERR! node_modules/react
npm ERR! peerOptional react@"^18.0.0 || ^19.0.0" from framer-motion@11.15.0
npm ERR! node_modules/framer-motion
npm ERR! peer framer-motion@">=11.5.6 || >=12.0.0-alpha.1" from @nextui-org/react@2.6.10
npm ERR! node_modules/@nextui-org/react
npm ERR! @nextui-org/react@"*" from the root project
npm ERR! peer framer-motion@">=11.5.6 || >=12.0.0-alpha.1" from @nextui-org/accordion@2.2.6
npm ERR! node_modules/@nextui-org/accordion
npm ERR! @nextui-org/accordion@"2.2.6" from @nextui-org/react@2.6.10
npm ERR! node_modules/@nextui-org/react
npm ERR! @nextui-org/react@"*" from the root project
npm ERR! 17 more (@nextui-org/system, @nextui-org/autocomplete, ...)
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR!
npm ERR!
npm ERR! For a full report see:
npm ERR! /Users/nitta/.npm/_logs/2025-01-05T10_24_55_348Z-eresolve-report.txt
npm ERR! A complete log of this run can be found in: /Users/nitta/.npm/_logs/2025-01-05T10_24_55_348Z-debug-0.log
どうも、このNext.jsアプリケーションでインストールされてるReactのバージョンが高すぎるらしい。
npm install react@18 react-dom@18
でReactをダウングレードしてからもう一度Next UIをインストールすると上手くいきました。
Next UIのCLIをインストール
npm install -g nextui-cli
これでnextui
コマンドが使えるようになりました。使わなくてもいいんじゃないかと思うけど公式が推してるので一応入れました。
モーダルをインストール
nextui add modal
これでpackage.jsonにモーダルのパッケージが追加されました。
ルートのDOMをNext UI Providerでラップ
これがNext.js初心者的に「??」な部分だったんだけど、Next UI ProviderっていうReactコンポーネントでDOMをラップする必要があります。多分、ライブラリを適用したいコンポーネントだけラップしてもいいんだと思うけど、公式が推してるようにルートのDOMをラップしておけばサイト全体で使えるので私もそうしました。App routerの場合、src/app/layout.tsx
がルートのDOMなので、そのbody
タグの中身の子要素をラップしました。(コメントしてる部分が既存のlayout.tsx
から書き換えた部分)
"use client"; ← これがないとダメだった
import Footer from "@/app/_components/footer";
...
import { NextUIProvider } from "@nextui-org/react"; ← Next UI Providerをインポート
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<head>
...
</head>
<body
className={cn(inter.className, "dark:bg-slate-900 dark:text-slate-400")}
>
<ThemeSwitcher />
<NextUIProvider><div className="min-h-screen">{children}</div></NextUIProvider> ← 子要素をラップ
<Footer />
</body>
</html>
);
}
use client
はいまいちよくわかってないので時間があるときに調べたい。
モーダル用のコンポーネントを作成
Next UI Modalの公式ドキュメントを読みつつ、最初はGalleryページapp/gallery/page.tsx
にそのままModalコンポーネントを入れようとしたんですが、Modal使用時に必要な"use client";
が既存のコードと喧嘩してうまくいかなかったので、app/_components/gallery-modal.tsx
にコンポーネントを作り、app/gallery/page.tsx
から呼び出すようにするとうまくいきました。やっぱuse client
は闇ですね。
モーダルコンポーネント app/_components/gallery-modal.tsx
"use client";
import {
Modal,
ModalContent,
ModalHeader,
ModalBody,
ModalFooter,
Button,
useDisclosure,
} from "@nextui-org/react";
type Props = {
src: string;
}
export default function GalleryModal({ src }: Props) {
const { isOpen, onOpen, onOpenChange } = useDisclosure();
return (
<>
<Button onPress={onOpen}><img className="h-auto max-w-full rounded-lg" src={`${src}`} /></Button>
<Modal
isOpen={isOpen}
onOpenChange={onOpenChange}
size="5xl"
hideCloseButton={true}
>
<ModalContent>
{(onClose) => (
<ModalBody>
<img className="h-auto max-w-full rounded-lg" src={`${src}`} />
</ModalBody>
)}
</ModalContent>
</Modal>
</>
);
}
呼び出し側 app/gallery/page.tsx
import Container from "@/app/_components/container";
import Header from "@/app/_components/header";
import PageTitle from "@/app/_components/page-title";
import fs from "fs";
import { join } from "path";
import GalleryModal from "@/app/_components/gallery-modal";
const coverImagesDirectory = join(process.cwd(), "public/assets/cover_images");
export default function Gallery() {
const files = fs.readdirSync(coverImagesDirectory);
return (
<main>
<Container>
<Header />
<PageTitle title="Gallery" />
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
{files.map((item) => (
<GalleryModal key={item} src={`/assets/cover_images/${item}`}/>
))}
</div>
</Container>
</main>
);
}
出来上がり!
これで画像クリックで拡大表示できるギャラリーができました。
画像をクリックすると...↓
ぱっ!!(ワァァァ〜〜)
実際のギャラリーはこちらになります。→ Gallery
これはモーダル、誰がどう見ても立派なモーダルです。やってやりました。バックエンド界のフロント音痴史に残る快挙と言えるでしょう。これは調子に乗ってなんかいろいろ作りたくなりますね。画面動かすのって面白いかも??