アカウントメニューの作成
パッケージのインストール
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を作成してください。
use clieant;を追加する。- Hydration error 回避の処理を追加する。
- 余分な項目を削除する。
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 にアクセスし、ヘッダ右上のアイコンをクリックすると、アカウントメニューが表示されます。