Next.js を使ってアプリケーションを開発していると、Text content did not match
というエラーに遭遇することがあります。これは React が サーバーサイドレンダリング (SSR) で生成した HTML と、クライアントが初回描画で生成した HTML のテキストが一致しない場合に発生する典型的な問題です。特に日付やロケール依存のフォーマット、ランダム値や環境差による違いが原因で起こることが多く、初心者から中級者まで多くの開発者を悩ませています。
本記事では、その原因と解決法を具体例とともに解説します。
1. エラーメッセージの例
実際に Next.js でこのエラーが発生すると、以下のようなメッセージが表示されます。
Warning: Text content did not match. Server: "2023/09/30" Client: "9/30/2023"
この例では、サーバーとクライアントで日付のフォーマットが異なっているため、React が差異を検知して Hydration に失敗しています。つまり、toLocaleDateString()
などの API がサーバーとクライアントで異なるロケール設定を利用したことが原因です。
2. よくある原因
このエラーが発生する典型的なケースは以下の通りです。
- 日付や時刻のフォーマット
new Date().toLocaleString()
を使った場合、サーバーとクライアントでタイムゾーンやロケールが異なると出力結果が変わります。 - ランダム値やユニーク ID
Math.random()
やDate.now()
を直接描画に使うと、サーバーとクライアントで異なる値が出力されます。 - ブラウザ固有の挙動
navigator.language
やwindow.innerWidth
のようにブラウザ依存の値を SSR 時に利用すると、一致しなくなります。 - 条件付き属性の差異
className
やaria-属性
などをサーバーとクライアントで異なる条件で付与すると mismatch が発生します。
3. 再現コード例
以下は、日付フォーマットによって mismatch が起きる例です。
// pages/index.tsx
export default function Home() {
const today = new Date().toLocaleDateString();
return <div>Today is {today}</div>;
}
このコードでは、サーバーのロケール設定が en-US
、クライアントが ja-JP
であれば、表示内容が異なりエラーになります。
解決策として、SSR でのレンダリングに依存させず、クライアント側でのみ日付を生成する方法があります。
// pages/index.tsx
import { useEffect, useState } from "react";
export default function Home() {
const [today, setToday] = useState("");
useEffect(() => {
setToday(new Date().toLocaleDateString());
}, []);
return <div>Today is {today}</div>;
}
この方法では、初期レンダリングでは空文字列が描画され、クライアント側で値が埋め込まれるため mismatch を避けられます。
4. 対策パターン
具体的な対策方法を整理すると以下の通りです。
- クライアント専用レンダリングに切り分ける
useEffect
を用いて、SSR では空状態にしておき、クライアントで値を補う。 - 環境依存を排除する
Intl.DateTimeFormat("en-US")
のようにロケールを明示的に指定し、サーバーとクライアントで同じ結果になるようにする。 - 条件付きレンダーの工夫
suppressHydrationWarning
を使って一部の mismatch を無視する。ただし乱用は推奨されません。 - ビルド時に固定する
変動する値を SSR で扱わず、ビルド時に静的に埋め込むか API 経由で取得する。
まとめ
Next.js / React で発生する Text content did not match
エラーは、サーバーとクライアントで出力される DOM が異なること が原因です。特に日付やロケール、ランダム値など環境依存のデータがよく問題を引き起こします。
解決策としては、クライアント専用レンダリングへの分離、ロケールの明示的指定、suppressHydrationWarning
の活用などがあります。プロジェクトの要件に応じて適切な方法を選択し、再現性の高いレンダリングを心がけることが重要です。
参考
- React 公式ドキュメント Hydration Errors [https://react.dev/reference/react-dom/client/hydrateRoot]
- Next.js Docs on Hydration [https://nextjs.org/docs/messages/react-hydration-error]
- MDN Date.prototype.toLocaleDateString [https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString]