はじめに
Next.js/React固有のエラーのひとつとして以下のようなHydrationエラーがあります。
Hydration failed because the server rendered HTML didn't match the client.
As a result, this tree will be regenerated on the client.
This can happen if a SSR-ed Client Component is used.
このエラーは、サーバーサイドとクライアントサイドでレンダリングされるHTMLの構造が一致しない場合に発生します。
今回、このエラーを解消していく中でHydrationについて理解を深めることができたので、一連の流れをまとめようと思います。
最初に遭遇したHydrationエラー
APIから取得したデータに基づいて画面のコンテンツを動的に表示する機能を実装しているときにHydrationエラーが発生しました。
これは、データを取得するまでの間にサーバーサイドとクライアントサイドでレンダリングされる内容にズレが生じていたことが原因だと考えられます。
そこで、値を取得するまでの間にMUIのCircularProgress
を使ってローディングUIを表示することで回避しようと以下のように実装しました。
'use client';
import CircularProgress from '@mui/material/CircularProgress';
import Box from '@mui/material/Box';
export default function DataDisplayPage() {
const { data } = useDataFetching();
// データがまだ取得できていない場合はローディング表示
if (!data) {
return (
<Box>
<CircularProgress />
</Box>
);
}
// データが正常に取得できた場合はメインコンテンツを表示
return (
<Box>
{/* メインコンテンツ */}
</Box>
);
}
ひとまずこれでエラーは出なくなりました!
問題の再発:見た目を改善しようとして...
しかし、このままだとローディングUIが画面左上に表示されてしまい、やや見た目がよくない状態でした。
そこで画面中央にローディングUIが表示されるように以下のように修正を加えました。
if (!data) {
return (
<Box
sx={{
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
}}
>
<CircularProgress />
</Box>
);
}
すると...Hydrationエラーが復活!😱
UIのスタイルを変更しただけなのに...?
原因を調査
2025/09/05 追記
記事公開後にコメントで指摘をいただき、根本的な原因はMUIのセットアップの問題であることが分かりました。
当初の内容は参考として残していますが、根本原因ではありませんでした。
【参考】当初の原因調査と解決策
原因について調べてみたところ、以下のようなことが分かりました。
- スタイルを指定しない場合はサーバーサイドもクライアントサイドも同じHTML/CSSになるためHydrationエラーは発生しない
-
position: fixed
やtransform
などを使うと、親要素やレイアウトの影響を受けやすくなるため、サーバーサイドとクライアントサイドで異なるDOM構造やスタイルが生成されることがある - サーバーサイドではウィンドウサイズなどの情報がないため、クライアントサイドで再計算されるとズレが生じる場合もある
つまり、クライアント固有のスタイル指定によってサーバーサイドとクライアントサイドで描画結果にズレが生じていたというのが今回の原因のようでした。
UIのスタイルを変えただけでエラーが発生するという経験をしたことがなかったので少し驚きましたが、仕組みを理解すれば納得できました。
解決策(訂正前)
対応方法はいくつかあると思いますが、今回は以下のようにスタイル指定なしのLinearProgress
を配置することで、画面上部にライン状のローディング表示をして対応しました。
これによって自然なローディング表示をしつつHydrationエラーを解消できました。
if (!data) {
return (
<Box>
<LinearProgress />
</Box>
);
}
解決策(訂正版)
MUIをNext.js App Routerで使用する場合、Next.js公式ドキュメントにある通り以下の手順で正しく設定する必要があります。
1. Next.js用のMUIパッケージをインストール
npm install @mui/material-nextjs
2. layout.tsxでAppRouterCacheProviderを設定
import { AppRouterCacheProvider } from '@mui/material-nextjs/v15-appRouter';
// or `v1X-appRouter` if you are using Next.js v1X
export default function RootLayout(props) {
return (
<html lang="en">
<body>
<AppRouterCacheProvider>
{props.children}
</AppRouterCacheProvider>
</body>
</html>
);
}
なぜこの設定が必要なのか
MUIは内部でemotion(CSS-in-JS)を使用してスタイルを生成しています。サーバーサイドレンダリング時には、emotionのキャッシュを適切に管理する必要があります。
AppRouterCacheProvider
は以下の役割を果たします:
- サーバーサイドでのスタイル生成とキャッシュ管理
- クライアントサイドでのHydration時のスタイル整合性の確保
- emotionのサーバー/クライアント間でのスタイルキャッシュの同期
この設定により、ローディングUIのスタイル指定に関係なく、Hydrationエラーが根本的に解消されます。
参考
Next.jsのlayout.tsx でMUIを使うとHydrationエラーになる?!
まとめ
- MUIをNext.jsで使用する際は、適切なセットアップをする必要がある
- CSS-in-JSライブラリを使用する場合、サーバーサイドレンダリングとの整合性に注意
コメントでのご指摘ありがとうございました。
同じような問題に遭遇した方の参考になれば幸いです。