🎨
MUIでcolorModeに応じたロゴの出し分けをserversideでやる
概要
Next.js & MUI環境でWebページを作っていたところ、darkModeの時にロゴの画像を切り替える部分で詰まってしまいました。
useColorScheme
を使えばクライアントサイドで値を取り出すことはできますが、最初のレンダリング時にundefinedが入る仕様になっているため、他のMUIコンポーネントに対してラグが生まれてしまいます。
The mode is always undefined on first render, so make sure to handle this case as shown in the demo below—otherwise you may encounter a hydration mismatch error.
"use client";
import Image from "next/image";
import { Box } from "@mui/system";
import { BoxProps } from "@mui/system/Box/Box";
import { useColorScheme } from "@mui/material/styles";
export type LogoProps = BoxProps;
export default function Logo(props: LogoProps) {
const { mode, systemMode } = useColorScheme();
const calculatedMode = systemMode ?? mode;
// 取得できるまでハンドリングするか、デフォルトを決めるか...
// if (!calculatedMode) return <></>;
return (
<Box
{...props}
sx={{
position: "relative",
}}
>
<Image
src={mode === "dark" ? "/images/logo_white.png" : "/images/logo_black.png"}
fill
alt={"Logo"}
style={{
objectFit: "contain",
}}
/>
</Box>
);
}
実装
next/image
を諦めて、 theme.applyStyles()
を使ってCSS側で対応しました。
import { Box } from "@mui/system";
import { BoxProps } from "@mui/system/Box/Box";
export type LogoProps = BoxProps;
export default function Logo(props: LogoProps) {
return (
<Box
{...props}
sx={[
(theme) => ({
backgroundImage: "url(/images/logo_black.png)",
backgroundSize: "contain",
backgroundPosition: "center",
backgroundRepeat: "no-repeat",
}),
(theme) =>
theme.applyStyles("dark", {
backgroundImage: "url(/images/logo_white.png)",
backgroundSize: "contain",
backgroundPosition: "center",
backgroundRepeat: "no-repeat",
}),
]}
/>
);
}
まとめ
-
theme.applyStyles
を使えばSSR上でもバチバチにハンドリングできて便利(Hydration問題もない) -
next/image
の恩恵から外れてしまったので、そこが解決できたらもっと嬉しい
Discussion