Hydration mismatchが起きる本当の理由

SSRで起きるHydration mismatchとは何か

SSR(Server Side Rendering)を導入すると、多くの開発者が一度は遭遇するのが「Hydration mismatch」です。コンソールに警告が出たり、特定の環境だけ表示が崩れたり、クリックできない要素が現れたりします。原因が分からず、再現もしづらい厄介な問題です。

これはReactやVueのバグではありません。サーバレンダリングとブラウザレンダリングの時間差によって必然的に発生する現象です。

結論から言うと、Hydration mismatchは「HTMLが違うから」起きるのではありません。レンダリングが行われたタイミングが違うから起きます。

まずHydrationの前提を確認する

SSRでは、サーバがHTMLを生成して返します。

<div id="app">
  <p>ようこそ、太郎さん</p>
</div>

ブラウザはこれを即表示します。その後JavaScriptが読み込まれ、同じコンポーネントが実行されます。そしてフレームワークは「サーバが作ったDOM」と「クライアントが生成するはずのDOM」を比較します。

このとき完全一致していることが前提です。一致していれば既存DOMを再利用します。違っていれば再生成します。これがHydrationです。

mismatchはなぜ起きるのか

重要なのは、サーバとブラウザは同時に動いていないことです。

サーバ:
リクエスト受信 → 即レンダリング

ブラウザ:
HTML受信 → JSダウンロード → JS実行 → レンダリング

ここには数百ms〜数秒の時間差があります。この間に状態が変化すると、出力結果が変わります。

代表的な原因:時刻

最も典型的な例です。

<p>{new Date().toLocaleTimeString()}</p>

サーバで描画された時刻と、ブラウザでHydrationが実行された時刻は一致しません。1秒でもずれるとDOMは一致しなくなります。

これがHydration mismatchの最も多い原因です。

乱数も危険

<p>ID: {Math.random()}</p>

サーバとクライアントで同じ値になる保証がありません。これも一致しません。

環境依存値

次も頻発します。

const isMobile = window.innerWidth < 768;

サーバは画面サイズを知りません。代替値でレンダリングします。ブラウザでは実際のサイズで描画されます。結果が変わります。

同様に影響するもの:

  • タイムゾーン
  • ロケール
  • ブラウザ言語
  • cookie状態
  • ダークモード設定

非同期データ

さらに厄介なのがAPIです。

サーバはデータAで描画、ブラウザでは最新データBで描画、という差が発生します。特に在庫数、通知数、セッション情報で起きやすいです。

<p>通知: {notifications.length}</p>

Hydration中にデータが更新されると不一致になります。

mismatchが起きると何が起きるか

フレームワークは安全のため、既存DOMを破棄し再描画します。その結果:

  • 画面が一瞬切り替わる
  • 入力内容が消える
  • イベントが外れる
  • パフォーマンスが悪化する

つまり、SSRの利点が失われます。

なぜ開発環境では起きにくいのか

開発環境ではサーバとブラウザが同一PC上にあり、実行タイミング差が小さいためです。本番ではネットワーク遅延により差が拡大します。モバイル回線で顕著になります。

対策の基本方針

重要なのは「サーバとクライアントで同じ結果を出す」ことです。

1. 時刻を描画しない**

サーバでは固定値を使い、クライアントで更新します。

2. 乱数を使わない**

IDはサーバで生成し、propsとして渡します。

3. 環境依存処理を遅延**

useEffect(() => {
  setIsMobile(window.innerWidth < 768);
}, []);

Hydration後に実行すれば不一致は起きません。

4. クライアント専用コンポーネント**

ブラウザ依存UIはSSRから除外します。

注意点

「suppressHydrationWarning」で警告を消す方法がありますが、問題を隠すだけです。根本的にはDOMが一致していません。イベントバグの原因になります。

最後に

Hydration mismatchはフレームワーク特有の不具合ではありません。SSRという仕組みが持つ時間差の副作用です。

SSRは「同じコードを2回実行する」技術です。1回目はサーバ、2回目はブラウザです。この2つが完全に同じ結果になると仮定して設計しないと、必ず不整合が起きます。

SSRを導入するとは、HTMLを返すことではなく「2つの実行環境で同じプログラムを動かすこと」です。Hydration mismatchを防ぐ鍵は最適化ではなく、時間に依存しないUI設計にあります。