😇

Vike + TypeScript + React + MUI での SSR エラー解決方法

2025/02/11に公開

🗂️ 背景

Vike、TypeScript、React を使用してサイトを開発中に、ヘッダーで MUI のアイコンを使用した際、以下のようなエラーが発生しました。

TypeError: deepmerge is not a function
    at createPalette (node_modules/@mui/material/styles/createPalette.js:244:25)
    at createThemeNoVars (node_modules/@mui/material/styles/createThemeNoVars.js:27:19)
    at createTheme (node_modules/@mui/material/styles/createTheme.js:53:14)

最初は @mui/material のコンポーネントが原因かと思い確認を進めましたが、実際には以下のような @mui/icons-material のアイコン使用部分 が原因でした。

import MenuIcon from "@mui/icons-material/Menu";

<IconButton color="inherit" edge="start" onClick={handleDrawerToggle} sx={{ mr: 2, display: { sm: "none" } }}>
    <MenuIcon />
</IconButton>

このコードが SSR(サーバーサイドレンダリング)環境で問題を引き起こしていました。


🚀 解決策

Vite の設定ファイル vite.config.ts を以下のように修正することで、エラーは解決しました。

import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import vike from "vike/plugin";
import { cjsInterop } from "vite-plugin-cjs-interop";
import { resolve } from "path";

export default defineConfig({
  plugins: [
    vike(),
    react(),
    cjsInterop({
      dependencies: ["@mui/material/**", "@mui/icons-material/**"],
    }),
  ],
  ssr: {
    noExternal: [
      "@mui/icons-material", // ✅ MUI のアイコンライブラリを内部バンドル
      "@mui/utils",          // ✅ MUI のユーティリティ関数もバンドル
    ],
  },
  build: {
    target: "es2022",
  },
  resolve: {
    alias: {
      "@": resolve(__dirname),
    },
  },
});

🔍 エラーの原因と詳細

原因 1: CommonJS と ES Modules の互換性問題

MUI の内部は ESM(ECMAScript Modules)CJS(CommonJS) が混在しています。Vite は ESM を標準サポートしているため、CJS のモジュールを正しく処理できない場合があります。

そのため、vite-plugin-cjs-interop を使用して、CJS モジュールを ESM 環境で適切に扱えるよう変換しています。

cjsInterop({
  dependencies: ["@mui/material/**", "@mui/icons-material/**"],
});
  • dependencies に対象パッケージを指定することで、互換性エラーを回避。
  • MUI の内部モジュールすべてに適用するため、/** でワイルドカード指定しています。

原因 2: SSR 環境での依存関係の外部化問題

Vite はデフォルトで node_modules 内のライブラリを 外部依存(external) として処理します。

しかし、MUI では @mui/utils@mui/icons-material などが内部で CommonJS 形式 で書かれており、Vite がこれらを正しくバンドルできずにエラーが発生します。

そのため、ssr.noExternal に指定することで内部バンドルさせ、エラーを回避します。

ssr: {
  noExternal: [
    "@mui/icons-material", // ✅ SSR 環境で外部化せず内部でバンドル
    "@mui/utils",          // ✅ MUI のユーティリティ関数も内部で処理
  ],
},
  • noExternal に追加することで、Vite が依存関係を外部化せず、自前で解決します。
  • 特に SSR 環境で 「Cannot destructure property」エラー の防止に効果的です。

🚀 最終的なまとめ

問題 解決策 効果
CJS と ESM の互換性エラー vite-plugin-cjs-interop を導入 CommonJS のモジュール互換性を確保
SSR での依存関係解決エラー ssr.noExternal に MUI パッケージを追加 SSR 環境でのモジュール解決エラーを防止
Vite のデフォルト外部化処理の問題 @mui/utils@mui/icons-material を内部化 依存関係の不整合解決

🔗 参考資料


MUI と Vite の組み合わせは柔軟で強力ですが、依存関係のバンドリングに注意が必要です。今回の設定で、安定した開発環境とパフォーマンスの向上が実現できます! 🚀

Discussion