1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【React×Tailwind CSS】Tailwind CSSプロジェクト内でHeadless UIを活用してモーダルを効率的に作成する

Last updated at Posted at 2025-09-05

はじめに

ReactとTailwind CSSを用いて課題のアプリを作成する機会がありました。
その中でモーダルを実装する機会があったのですが、Tailwind CSSのみでモーダルを作成するのはかなりの工数が発生する上に実装も一筋縄でいかないことがわかりました。

そのため今回はHeadless UIを採用しました。

Headless UIとは

Headless UIは特定のスタイルを持たず、UIの機能部分のみを抽出したUIコンポーネントです。
下記の説明通り、スタイルはTailwind CSSで設定することを想定されています。

Completely unstyled, fully accessible UI components, designed to integrate beautifully with Tailwind CSS.
(完全にスタイル設定されていない、完全にアクセス可能な UI コンポーネント。Tailwind CSS と美しく統合されるように設計されています。)

Headless UIを使ってみる

はじめにHeadless UIをnpmでインストールします。

npm install @headlessui/react

Headless UIの公式サイトにはUIコンポーネントの機能別サンプルがいくつか掲載されており、よく使われるUIのアイテムは一通り網羅されています。
今回のようなモーダルの場合はDialogを選択します。

最小単位のサンプルコードはHeadless UIのページ内に記載されているので、コピペすることでそのまま最小単位のUIを実装することができます。

公式引用:最小単位のモーダルUI
import { Description, Dialog, DialogPanel, DialogTitle } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  let [isOpen, setIsOpen] = useState(false)

  return (
    <>
      <button onClick={() => setIsOpen(true)}>Open dialog</button>
      <Dialog open={isOpen} onClose={() => setIsOpen(false)} className="relative z-50">
        <div className="fixed inset-0 flex w-screen items-center justify-center p-4">
          <DialogPanel className="max-w-lg space-y-4 border bg-white p-12">
            <DialogTitle className="font-bold">Deactivate account</DialogTitle>
            <Description>This will permanently deactivate your account</Description>
            <p>Are you sure you want to deactivate your account? All of your data will be permanently removed.</p>
            <div className="flex gap-4">
              <button onClick={() => setIsOpen(false)}>Cancel</button>
              <button onClick={() => setIsOpen(false)}>Deactivate</button>
            </div>
          </DialogPanel>
        </div>
      </Dialog>
    </>
  )
}

引用:https://headlessui.com/react/dialog

Headless UIのコンポーネントには必要なUIの機能は一通り備わっているため、素のHTMLとJSでは実装に手間取る「モーダルの外側をクリックすると自動で閉じる」といった機能も搭載済みです。

Dialogのコンポーネントの役割は以下の通りでした。

  • <Dialog>:モーダル全体の機能に関わる部分。opencloseの処理の関数を設定したりする
  • <DialogPanel>:モーダルの内側を囲っている要素。モーダルの背景色やpadding、閉じるときのモーダルの動きをTailwind CSSを使って制御できる

下記はモーダルが閉じる際のトランジションを加えた例。
(可読性の関係で改行を加えています)

<DialogPanel transition 
className="duration-300 ease-out data-closed:transform-[scale(95%)] data-closed:opacity-0">
    {/* ここにモーダルの中身が入ります */}
</DialogPanel>
  • <DialogTitle>:見出しに相当する部分

イメージが掴みにくい場合は公式ドキュメントに作例が記載されているのでそちらを参考にしてみてください。

使ってみて感じた感想・長所

Headless UIを使って実装することでデザインの自由度が高くなることが最大の長所に感じました。
全体を通してUIやデザインの重要性・比重が高いプロジェクトでは強い味方になってくれる気がしています。

一方でUIやデザインに強いこだわりがあるわけではない案件の場合ではHeadless UIではなく別のUIコンポーネント(Chakra UIなど)を使った方が楽に実装できるようにも感じました。

最後に

モーダルをはじめとしてTailwind CSSにプロジェクトで手軽にUIを実装したい場合はHeadless UIが強い味方になってくれました。
同じような場面に遭遇したら次回以降も採用しようと思います。

参考資料

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?