View on GitHub

アカウントメニューの作成

Home

アカウントメニューの作成

パッケージのインストール

frontend$
 yarn add @mui/material
 yarn add @mui/icons-material
 # ...(5分程度)...
 yarn add @emotion/react
 yarn add @emotion/styled

frontend$ less package.json
...(略)...
  "dependencies": {
    "@emotion/react": "^11.14.0",     # <-
    "@emotion/styled": "^11.14.1",    # <-
    "@mui/icons-material": "^9.4.0",  # <-
    "@mui/material": "^9.4.0",        # <-
    "next": "16.3.3",
    "prettier": "^3.9.6",
    "prettier-plugin-tailwindcss": "^0.8.1",
    "react": "19.2.8",
    "react-dom": "19.2.8"
  },
...(略)...

アカウントメニュー

Material UI のAccount menuをベースにAccountMenu.tsxを作成してください。

src/frontend/src/app/components/ui/header/AccountMenu.tsx

"use client"; // <- 追加

import * as React from "react";
import Box from "@mui/material/Box";
import Avatar from "@mui/material/Avatar";
import Menu from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem";
import ListItemIcon from "@mui/material/ListItemIcon";
import Divider from "@mui/material/Divider";
import IconButton from "@mui/material/IconButton";
// import Typography from '@mui/material/Typography'; // <- 削除
import Tooltip from "@mui/material/Tooltip";
// import PersonAdd from '@mui/icons-material/PersonAdd'; // <- 削除
// import Settings from '@mui/icons-material/Settings'; // <- 削除
import Logout from "@mui/icons-material/Logout";

export default function AccountMenu() {
  const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
  const [isClient, setIsClient] = React.useState(false); // <- 追加

  // ↓追加
  // Hydration error回避のため、クライアントサイドのみで状態を設定する。
  React.useEffect(() => {
    setIsClient(true);
  }, []);
  // ↑追加

  const open = Boolean(anchorEl);
  const handleClick = (event: React.MouseEvent<HTMLElement>) => {
    setAnchorEl(event.currentTarget);
  };
  const handleClose = () => {
    setAnchorEl(null);
  };

  // ↓追加
  // クライアントサイドでのみレンダリングする部分を分ける。
  if (!isClient) {
    return null; // サーバサイドでは何もレンダリングしない。
  }
  // ↑追加

  return (
    <React.Fragment>
      <Box sx={{ display: "flex", alignItems: "center", textAlign: "center" }}>
        {/* ↓削除
        <Typography sx={{ minWidth: 100 }}>Contact</Typography>
        <Typography sx={{ minWidth: 100 }}>Profile</Typography>
        ↑削除 */}
        <Tooltip title="Account settings">
          <IconButton
            onClick={handleClick}
            size="small"
            sx={{ ml: 2 }}
            aria-controls={open ? "account-menu" : undefined}
            aria-haspopup="true"
            aria-expanded={open}
          >
            <Avatar sx={{ width: 32, height: 32 }}>M</Avatar>
          </IconButton>
        </Tooltip>
      </Box>
      <Menu
        anchorEl={anchorEl}
        id="account-menu"
        open={open}
        onClose={handleClose}
        onClick={handleClose}
        slotProps={{
          paper: {
            elevation: 0,
            sx: {
              overflow: "visible",
              filter: "drop-shadow(0px 2px 8px rgba(0,0,0,0.32))",
              mt: 1.5,
              "& .MuiAvatar-root": {
                width: 32,
                height: 32,
                ml: -0.5,
                mr: 1,
              },
              "&::before": {
                content: '""',
                display: "block",
                position: "absolute",
                top: 0,
                right: 14,
                width: 10,
                height: 10,
                bgcolor: "background.paper",
                transform: "translateY(-50%) rotate(45deg)",
                zIndex: 0,
              },
            },
          },
        }}
        transformOrigin={{ horizontal: "right", vertical: "top" }}
        anchorOrigin={{ horizontal: "right", vertical: "bottom" }}
      >
        {/* ↓削除
        <MenuItem onClick={handleClose}>
          <Avatar /> Profile
        </MenuItem>
        ↑削除 */}
        <MenuItem onClick={handleClose}>
          <Avatar /> My account
        </MenuItem>
        <Divider />
        {/* ↓削除
        <MenuItem onClick={handleClose}>
          <ListItemIcon>
            <PersonAdd fontSize="small" />
          </ListItemIcon>
          Add another account
        </MenuItem>
        <MenuItem onClick={handleClose}>
          <ListItemIcon>
            <Settings fontSize="small" />
          </ListItemIcon>
          Settings
        </MenuItem>
        ↑削除 */}
        <MenuItem onClick={handleClose}>
          <ListItemIcon>
            <Logout fontSize="small" />
          </ListItemIcon>
          Logout
        </MenuItem>
      </Menu>
    </React.Fragment>
  );
}

ヘッダ

src/frontend/src/app/components/Header.tsx

import Link from 'next/link';

import { STYLES, SETTINGS } from '@/constants';

import AccountMenu from './ui/header/AccountMenu'; // <- 追加

const Header = () => {
  return (
    ......
        <nav className={`${STYLES.HEADER_MENU}`}>
          <Link href="/about/">About</Link>
          <div>Sign Up</div>
          <div>Sign In</div>
          <AccountMenu /> {/* <- 追加 */}
        </nav>
    ...(略)...
  );
};

export default Header;

ブラウザで下記 URL にアクセスし、ヘッダ右上のアイコンをクリックすると、アカウントメニューが表示されます。

参考

  1. React Menu component - Material UI