旧ブログ(ISSEN)から移行しました

【React】MantineUIで作るハンバーガーメニュー

【React】MantineUIで作るハンバーガーメニュー

杉田侑祐
杉田侑祐6分で読めます

はじめに

この記事の概要

こんにちは、株式会社TOKOSのスギタです!
今回はTailwind CSSを用いてUIライブラリMantineを使いハンバーガーメニューを作成する方法を説明していきたいと思います。
Mantineについてわからない方は下記を参考にしてください!

対象読者

  • Reactを用いたWEB制作、WEB開発を行っている方
  • Tailwind CSSを使用しながらUIライブラリを使用したい方

開発環境

  • react: 18.0.28
  • next.js: 13.2.3
  • typescript: 4.9.5
  • @mantine/core: 6.0.13
  • @mantine/hooks: 6.0.13

下記のリンク先でフレームワークを選択してください。
今回はNext.jsで行っていきたいと思います!

https://mantine.dev/pages/getting-started/

mantine.dev

完成予定

今回の完成予定は下記になります。

ハンバーガーメニューの実装

まずはメニューボタンから作成します。
メニューボタンですが、MantineUIからBurgerコンポーネントを使用します。

Burger | Mantine

Open/close navigation button

mantine.dev
ExamplePage.tsx
import { Burger } from "@mantine/core"
 
const ExamplePage: NextPageWithLayout = () => (
  <header className="fixed z-20 flex w-full gap-y-2 bg-blue-500 p-4 text-white shadow">
    <div className="flex w-full max-w-[1366px] justify-between">
      <div className="flex w-full justify-between">
        <div>
          <Burger opened={false} color="#fff" />
        </div>
        <p className="flex w-full items-center justify-center text-xl font-bold">TOKOS.inc</p>
      </div>
    </div>
  </header>
)

propscolorで色の指定をしています。
まだ状態管理をしていないので、現状は動かないと思います。
ではここから状態管理を追加して、メニューを押した際のアニメーションを追加していきたいと思います。

ExamplePage.tsx
import { Burger } from "@mantine/core"
 
const ExamplePage: NextPageWithLayout = () => (
  <header className="fixed z-20 flex w-full gap-y-2 bg-blue-500 p-4 text-white shadow">
    <div className="flex w-full justify-between">
      <div className="flex w-full justify-between">
        <div>
          <Burger opened={true} color="#fff" />
        </div>
        <p className="flex w-full items-center justify-center text-xl font-bold">TOKOS.inc</p>
      </div>
    </div>
  </header>
)

propsopenedtrueに変えるとバツになると思います。
このpropsopenedが表示状態の管理をしていることがわかったと思います。
ではこのメニューを押した際にopenedの真偽値を切り替えていきたいので、propsonClickで押した際の動作を追記していきます。

また、今回は複数のコンポーネントの状態管理にuseDisclosureというMantineのCustom Hooksを使用していきます。

use-disclosure | Mantine

Manages boolean state, provides open, close and toggle handlers, usually used with modals, drawers and popovers

mantine.dev
ExamplePage.tsx
import { Burger } from "@mantine/core"
import { useDisclosure } from "@mantine/hooks"
 
const ExamplePage: NextPageWithLayout = () => {
  const [isHamburgerOpened, { toggle: hamburgerToggle }] = useDisclosure(false)
  return (
    <header className="fixed z-20 flex w-full gap-y-2 bg-blue-500 p-4 text-white shadow">
      <div className="flex w-full justify-between">
        <div className="flex w-full justify-between">
          <div>
            <Burger opened={isHamburgerOpened} onClick={hamburgerToggle} color="#fff" />
          </div>
          <p className="flex w-full items-center justify-center text-xl font-bold">TOKOS.inc</p>
        </div>
      </div>
    </header>
  )
}

BurgerコンポーネントのonClickuseDisclosuretoggle関数(hamburgerToggle)を渡します。
またopenedに、toggle関数によって切り替わった真偽値を渡すようにします。

CustomHooksのuseDisclosureですが、公式ドキュメントを見ると、真偽値の管理、openclosetoggleのハンドラーとonOpenonCloseのコールバックがあります。
今回はコールバック関数は使用しません。

useDisclosureの使い方
// 公式のハンドラーの説明より
import { useDisclosure } from "@mantine/hooks"
 
function Demo() {
  const [opened, handlers] = useDisclosure(false)
 
  // openedをtrueに
  handlers.open()
 
  // openedをfalseに
  handlers.close()
 
  // openedの真偽値を反転
  handlers.toggle()
}

今回は、押した時にtoggle関数(hamburgerToggle)を使用して、真偽値の反転をしています。
初期値はメニューが閉じた状態にしたいので、falseにしています。
これで、メニューボタンの実装は完了です。

次にMantineUIのDrawerコンポーネントを使用して、メニューの実装をします。

ExamplePage.tsx
import { Burger, Drawer } from "@mantine/core"
import { useDisclosure } from "@mantine/hooks"
 
const ExamplePage: NextPageWithLayout = () => {
  const [isHamburgerOpened, { toggle: hamburgerToggle }] = useDisclosure(false)
  return (
    <header className="fixed z-20 flex w-full gap-y-2 bg-blue-500 p-4 text-white shadow">
      <div className="flex w-full justify-between">
        <div className="flex w-full justify-between">
          <div>
            <Burger opened={isHamburgerOpened} onClick={hamburgerToggle} color="#fff" />
            <Drawer
              opened={isHamburgerOpened}
              zIndex={0}
              withCloseButton={false}
              classNames={{
                body: "p-0",
                inner: "w-[380px]",
              }}
            >
              <div className="px-4 pt-[78px] font-bold">
                <p className="mb-4">マイページ</p>
                <p className="mb-4">ログアウト</p>
                <p>ホーム</p>
              </div>
            </Drawer>
          </div>
          <p className="flex w-full items-center justify-center text-xl font-bold">TOKOS.inc</p>
        </div>
      </div>
    </header>
  )
}

まずはDrawerコンポーネントを@mantine/coreから呼び出します。
ヘッダーより下にしたいので、propszIndex0にします。
今回メニューの開閉はBurgerボタンで行うので、propswithCloseButtonfalseにします(デフォルトがtrue)。
propsclassNamesでデフォルトのpaddingを消し、メニューの幅を指定しています。

ここから、開く処理と閉じる処理を追記します。

ExamplePage.tsx
import { Burger, Drawer } from "@mantine/core"
import { useDisclosure } from "@mantine/hooks"
 
const ExamplePage: NextPageWithLayout = () => {
  const [isHamburgerOpened, { close: hamburgerClose, toggle: hamburgerToggle }] = useDisclosure(false)
  return (
    <header className="fixed z-20 flex w-full gap-y-2 bg-blue-500 p-4 text-white shadow">
      <div className="flex w-full justify-between">
        <div className="flex w-full justify-between">
          <div>
            <Burger opened={isHamburgerOpened} onClick={hamburgerToggle} color="#fff" />
            <Drawer
              opened={isHamburgerOpened}
              onClose={hamburgerClose}
              zIndex={0}
              withCloseButton={false}
              classNames={{
                body: "p-0",
                inner: "w-[380px]",
              }}
            >
              <div className="px-4 pt-[78px] font-bold">
                <p className="mb-4">マイページ</p>
                <p className="mb-4">ログアウト</p>
                <p>ホーム</p>
              </div>
            </Drawer>
          </div>
          <p className="flex w-full items-center justify-center text-xl font-bold">TOKOS.inc</p>
        </div>
      </div>
    </header>
  )
}

useDisclosurecloseハンドラーを追記し、openedで管理されている真偽値を渡します。
Drawerが閉じる際に発火する関数としてuseDisclosurecloseハンドラーを渡します。
そうすることにより、メニューが開く際にisHamburgerOpenedtrue、メニューが閉じた際にisHamburgerOpenedfalseになるという切り替えが可能になります。

さいごに

今回はMantineのBurgerコンポーネントとDrawerコンポーネント、そしてCustomHooksのuseDisclosureを使用しました。
やはりかなり簡単に実装できるかなと思いました。
スクラッチで書こうとすると、overlayの箇所の動作や細かいアニメーションを考えるとかなり大変なので、こういう実装はUIライブラリに頼るのも手なのかなと感じます。

この記事を書いた人

杉田侑祐
杉田侑祐

TOKOSのフロントエンドエンジニア兼UI/UXデザイナー。このブログではフロントエンドメインで投稿しています。HIPHOPとゲームが好きです✌️