🎨

MUIでcolorModeに応じたロゴの出し分けをserversideでやる

2025/02/02に公開

概要

Next.js & MUI環境でWebページを作っていたところ、darkModeの時にロゴの画像を切り替える部分で詰まってしまいました。

https://mui.com/material-ui/customization/css-theme-variables/configuration/?srsltid=AfmBOoojtnhrRQ52rDhjc3BXv66DxT9K6W2ncvFjp40hO1k_2rwV2wbQ#toggling-dark-mode-manually

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側で対応しました。

https://mui.com/material-ui/customization/dark-mode/?srsltid=AfmBOooK9A3EzaRLMBpNYOprwpuEWlRncWIumujCInnUNDiGzQM0r4Bd#styling-in-dark-mode

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