Next.js / React でサーバーとクライアントで DOM 属性が異なり「Text content did not match」エラーになる原因と修正

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. よくある原因

このエラーが発生する典型的なケースは以下の通りです。

  1. 日付や時刻のフォーマット
    new Date().toLocaleString() を使った場合、サーバーとクライアントでタイムゾーンやロケールが異なると出力結果が変わります。
  2. ランダム値やユニーク ID
    Math.random()Date.now() を直接描画に使うと、サーバーとクライアントで異なる値が出力されます。
  3. ブラウザ固有の挙動
    navigator.languagewindow.innerWidth のようにブラウザ依存の値を SSR 時に利用すると、一致しなくなります。
  4. 条件付き属性の差異
    classNamearia-属性 などをサーバーとクライアントで異なる条件で付与すると 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. 対策パターン

具体的な対策方法を整理すると以下の通りです。

  1. クライアント専用レンダリングに切り分ける
    useEffect を用いて、SSR では空状態にしておき、クライアントで値を補う。
  2. 環境依存を排除する
    Intl.DateTimeFormat("en-US") のようにロケールを明示的に指定し、サーバーとクライアントで同じ結果になるようにする。
  3. 条件付きレンダーの工夫
    suppressHydrationWarning を使って一部の mismatch を無視する。ただし乱用は推奨されません。
  4. ビルド時に固定する
    変動する値を SSR で扱わず、ビルド時に静的に埋め込むか API 経由で取得する。

まとめ

Next.js / React で発生する Text content did not match エラーは、サーバーとクライアントで出力される DOM が異なること が原因です。特に日付やロケール、ランダム値など環境依存のデータがよく問題を引き起こします。

解決策としては、クライアント専用レンダリングへの分離、ロケールの明示的指定、suppressHydrationWarning の活用などがあります。プロジェクトの要件に応じて適切な方法を選択し、再現性の高いレンダリングを心がけることが重要です。

参考

タイトルとURLをコピーしました