Next.jsのhydration不一致を、初回HTMLとクライアント描画から調べる

Next.jsのhydration不一致を、初回の値、HTML構造、ブラウザー保存値、配信経路から切り分けます。

AIものさし編集部

Next.jsでhydrationの不一致が表示されたときは、サーバーが返した内容と、ブラウザーが最初に描画しようとした内容の差を調べます。画面が最終的に見えることや、警告を非表示にできたことだけでは、原因が直ったとは判断しません。AIに修正させる場合も、どの要素の初回描画が異なるかを先に説明させてください。

この記事はNext.jsとReactの公式資料に基づく診断手順です。掲載例をNext.jsで起動したり、ブラウザーでhydrationを再現したりした実測記事ではありません。適用前に、使用中のReact・Next.jsの版と、App Router/Pages Routerの構成を確認します。

比べるのは「最初の描画」同士

Next.jsのエラー説明(新しいタブで開く)は、サーバーで事前描画したReactツリーと、ブラウザーで最初に描画したツリーの差を原因として挙げています。読み込み後に状態が変わること自体と、最初の内容が一致しないことを分けます。

調査記録には、警告全文、最初に指摘されたコンポーネント、差が出た文字や属性、URL、使用版を残します。個人情報や認証値を含むHTMLを共有ログへそのまま貼る必要はありません。再読み込み、画面内の移動、ログイン状態の違いで条件が変わるなら、それぞれを別の観察として記録してください。

表は横にスクロールできます

観察した差調べる入口修正前に確認すること
日時や表示言語が違う現在時刻、地域・時差の設定両側へ同じ値・条件が渡っているか
保存済みの設定だけ違うlocalStorageなどの読み取り初回描画からブラウザー値を使っていないか
要素の並びや入れ子が違うHTML構造、条件分岐ブラウザーが解釈する前の構造は妥当か
特定端末だけ違う拡張機能や配信経路HTMLが途中で変更されていないか

この表は原因を確定するものではありません。画面の差を一つずつ、元の値と描画条件へ戻すための分類です。

日時・乱数・保存値を描画の途中で作らない

AI生成コードでは、便利な値をその場で求める処理がコンポーネントに入りがちです。たとえば現在日時、ランダム値、端末の表示言語、ブラウザーに保存された設定です。サーバーとブラウザーが同じ値を用意する契約がなければ、差が出る可能性を確認します。

表示が業務データを表すなら、まず同じデータ版を両側で使えるか考えます。たとえば注文の作成日時は、描画した時点の現在時刻へ置き換えず、注文レコードの値として渡します。日付だけでなく、表示するタイムゾーンや形式も決め、同じ値でも文字列化の条件が違わないかを見ます。

一方、端末だけに保存されたテーマ設定などは、サーバーが同じ値を知らない場合があります。そのときは、初回に共通で出す内容と、ブラウザー側の値を反映する時点を分ける方法を検討します。画面全体を空にするのではなく、差が生じる要素に範囲を絞ってください。

ブラウザーの値は初回の共通表示と分ける

次は、保存された表示設定を後から読む説明用の例です。初回の文字列を両側で固定し、Effectで読んだ後に状態を更新します。ReactのhydrateRoot説明(新しいタブで開く)も、初回をそろえてからEffectで表示を変える方法と、その追加描画の負担を説明しています。

'use client';

import { useEffect, useState } from 'react';

export default function SavedViewLabel() {
  const [label, setLabel] = useState('標準表示');

  useEffect(() => {
    try {
      const saved = window.localStorage.getItem('view-label');
      if (saved === 'compact') setLabel('簡易表示');
    } catch {
      // 保存領域を読めない場合は共通の初期表示を使う。
    }
  }, []);

  return <span>{label}</span>;
}

この例では、初回は「標準表示」、保存値がcompactならEffect後に「簡易表示」という設計です。実行結果を観測したものではありません。設定を反映するまで表示が変わるため、実装先ではちらつき、レイアウト変化、読み上げへの影響も確認します。

'use client'を追加しただけで初回描画の差が解決した、と決めないでください。確認するのは、その構成でサーバーの出力とクライアント初回の値がそろうかです。typeof windowで初回から違う内容を返す分岐も、差を作る原因になり得ます。

HTMLの入れ子と外部の書き換えを調べる

Next.jsの公式説明には、不正なHTMLの入れ子、拡張機能による変更、CSS-in-JSや配信側の設定も原因例として挙がっています。日時の処理ばかり直しても、実際の差が要素構造にあれば問題は残ります。

たとえば、文章用のp要素の中へdivを置いたり、リンクの中へ別のリンクを入れたりしていないかを確認します。再利用コンポーネントが内部で何のタグを出すかも見ます。見た目が同じ部品でも、呼び出し側と部品側の両方でbuttonを出していれば、意図しない入れ子になります。

拡張機能を無効にした条件や別の配信経路で差が消えた場合は、その条件を記録します。それは切り分けの材料であり、全利用者に拡張機能の停止を求めることが最終修正とは限りません。アプリのHTMLと、ブラウザーへ届いた後の変更を分けて追います。

警告抑制やSSR無効化は対象を限定する

suppressHydrationWarningは不一致の原因を直す機能ではありません。React公式は、一段の深さに限る例外手段で、合わないテキストを修復しようとしないことも説明しています。警告が消えたという理由だけで、時刻・金額・認証表示の食い違いを残さないようにします。

ブラウザーだけで意味を持つ部品なら、その部品に限って事前描画を使わない構成が適する場合もあります。ただし、ルーター構成、配置場所、使用版による制約を確認し、ページ全体のSSRを外して済ませないでください。初期表示や読み込み中の代替表示がどう変わるかも判断に含めます。

表は横にスクロールできます

対応の候補採用前の問い
同じデータを両側へ渡すデータ版と表示条件までそろっているか
初回をそろえEffect後に更新表示変化を許容できる要素か
部品単位で事前描画を外す初期表示と利用できるAPIの制約を確認したか
警告を限定的に抑える避けられない差と、その影響を説明できるか

修正の合否を警告件数だけで決めない

再検証は、原因を確認したURLと条件で行います。初回の直接アクセス、再読み込み、画面内移動で、期待する文字・要素・操作が維持されるかを確認してください。保存値がある場合とない場合、保存領域を読めない場合も、想定する対象なら確認します。

AIへの修正依頼には、差が生じた要素と入力値、元のHTML構造、採用した対処、その対処で変わる表示範囲を残します。「警告を消した」ではなく「初回の値をそろえ、必要な更新だけを後から行う」と説明できれば、後から別の開発者が意図を確認できます。実ブラウザーで未確認なら、その状態を完了記録に明記し、ソースの検査だけを表示成功とは扱わないでください。

確認した公式情報

確認日:2026-10-20。掲載例の起動・ブラウザー検証は未実施です。

質問から自分に合うAIを探す