React + Headless UI + Tailwind CSSでモーダルを作成してみた

ブログメインビジュアル

こんにちは。
今回はReact + Headless UI + Tailwind CSSでモーダルを作成してみたいと思います!

Headless UIとは、UIライブラリですがコンポーネントがデフォルトのスタイルを持っていないという特徴があります。
他のUIコンポーネントと比べ、自由にスタイルを設定することができ、スタイルがないので無駄なオーバーライドをする必要がなくなることがメリットとしてあります。
またユーティリティファーストであるTailwind CSSとの相性が良いとのことで、実際に使用してみたいと思います。

Tailwind CSSについて事前知識がない方は、こちらを一度ご覧ください。

それでは進めていきます!

目次

事前準備

事前にReact、Headless UI、Tailwind CSSそれぞれの環境を構築します。
React環境構築に関しては、こちらをご覧ください。
Tailwind CSSにの導入についてはこちらを参考に進めてください。



またHeadless UIを使用するため以下のパッケージをインストールします。

$ yarn add @headlessui/react

こちらにて事前の準備は完了です。

ディレクトリ構成

ディレクトリ構成は以下です。

root/
 ├── src/
    ├── components
        ├── Button/
            ├── index.js
        ├── Dialog/
            ├── index.js
    ├── App.js
    ├── index.css
    ├── index.js

実装

それでは実装に入っていきます!

Button

まずはボタンコンポーネントを作成します。
ボタンのスタイルはよしなに調整してください。

function Button(props) {
  return (
    <button
      className='bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded'
      onClick={props.onClick}
    >
      {props.children}
    </button>
  )
}

export default Button

Dialog

次にDialogを作成します。
Headless UIにDialogが用意されているのでこちらをimportし、スタイルは公式サイトを参考に調整します。
注意点としてDialogのままだとコンポーネント名とかぶってしまうのでHuiDialogとするようにしてます。

import { Dialog as HuiDialog } from '@headlessui/react'
import Button from '../Button'

function Dialog(props) {

  return (
    <HuiDialog
      open={props.isOpen}
      onClose={props.onClose}
      className='fixed z-10 inset-0 overflow-y-auto'
    >
      <div className='flex items-center justify-center min-h-screen'>
        <HuiDialog.Overlay className='fixed inset-0 bg-black opacity-30' />
        <div className='relative bg-white rounded max-w-lg mx-auto p-5'>
          <HuiDialog.Title
            as='h3'
            className='Description-lg font-medium leading-6 Description-gray-900'
          >
            {props.title}
          </HuiDialog.Title>
          <HuiDialog.Description className='mt-2 text-sm text-gray-500'>
            {props.description}
          </HuiDialog.Description>
          <div className='mt-4'>
            <Button onClick={props.onClose}>close</Button>
          </div>
        </div>
      </div>
    </HuiDialog>
  )
}

export default Dialog

上記では必要最低限の実装にしていますが、Headless UIで用意されているTransitionを使用するとモーダルの開閉にアニメーションをつけることが可能みたいです。興味のある方は調べてみてください!

App.js

最後にDialogをApp.js内で呼び出します。
今回はボタンクリック時にモーダルが開くようにしています。

import { useState } from 'react'
import Button from './components/Button'
import Dialog from './components/Dialog'

function App() {
  const [isModalOpen, setIsModalOpen] = useState(false)

  return (
    <div className='mt-5 ml-5'>
      <Button onClick={() => setIsModalOpen(true)}>Dialog Open</Button>
      <Dialog
        isOpen={isModalOpen}
        onClose={() => setIsModalOpen(false)}
        title='Dialog Title'
        description='Dialog Description. Dialog Description. Dialog Description. Dialog Description. Dialog Description.'
      ></Dialog>
    </div>
  );
}

export default App

完成

モーダル実装画面

上記が完成のキャプチャとなります!

まとめ

Headless UIを使用すればすぐにモーダルなどの実装は可能ですが、スタイルは1からTailwind CSSで調整する必要があるので、Tailwind CSSの書き方に慣れていないとなかなか大変だと感じました。
もう少し試しながら書き方を覚えていきたいと思います。
最後までお読みいただきありがとうございます。

この記事を書いた人 kito エンジニアになるために愛知から上京しました。
TOP