本文へ移動
verified onnext@16.3.5react@19
最終検証日 2026-09-26
Next.js道場

Hydration とは何か

Hydration は、サーバーが送った HTML に、ブラウザ側の React を接続して操作できるようにする処理です。

なぜ二度描くのか

hydration の流れ。同じ内容を二度描くことが前提になっている
  1. Server
    サーバーが HTML を作る

    早く表示するため。検索エンジンもこの HTML を読む。

  2. Browser
    HTML をそのまま表示

    文字も画像ももう見えている。まだ操作はできない。

  3. Browser
    React が同じ内容を描き直す

    サーバーの結果と突き合わせ、一致していればイベントを繋ぐ。

サーバーが HTML を送るだけなら、画面はすぐ出ます。ただし、そのままではボタンを押しても何も起きません。イベントを繋ぐ JavaScript が動いていないからです。

そこで、ブラウザ側でも同じ内容をもう一度描いて、送られてきた HTML に React を接続します。これが hydration です。

乾いた HTML に水を差して動くようにする、という比喩から来た名前です。

対象は Client Component だけ

ここを取り違えると、原因の見当がずれます。

サーバーで実行ブラウザで実行hydration の対象
Server Componentするしない対象外
Client Componentする(最初の1回)する対象

Client Component は「ブラウザだけで動く」ものではありません。「ブラウザにも送られる」ものです。最初の1回はサーバーでも実行されます。

だから、'use client' を書いたファイルの中で window を直接読むと、その1回目で落ちます。

食い違うと何が起きるか

サーバーが描いたものと、ブラウザが描いたものが違うと、React は「送られてきた HTML は当てにならない」と判断して、その部分を作り直します。

同じコードなのに結果が変わる。React はこの差を見つけて作り直す
サーバーが作った HTML
<p>2026-08-28T01:40:31.478Z</p>

描いた瞬間の時刻。HTML に焼き込まれて配られる。

ブラウザが描き直した結果
<p>2026-08-28T01:40:31.676Z</p>

表示した瞬間の時刻。198ミリ秒ずれている。ここで食い違いになる。

Next.js の開発オーバーレイは、これを差分で見せてくれます。+ がブラウザ側、- がサーバー側です。

ページは動き続けます(開発モードでは Recoverable Error と表示されます)。ただし、

  • 作り直しのぶん、表示が一瞬ちらつく
  • サーバーで HTML を作った意味が薄れる

ので、直すべき状態です。

食い違いを生まないための原則

最初の描画では、サーバーとブラウザで必ず同じものを描く。

これだけです。違ってよいのは hydration が終わったあと、つまり useEffect の中からです。

'use client'
 
import { useEffect, useState } from 'react'
 
export function Clock() {
  const [now, setNow] = useState<string | null>(null)
 
  useEffect(() => {
    setNow(new Date().toISOString())   // 接続後に入れる
  }, [])
 
  return <p>{now ?? '—'}</p>           // 最初は両方 '—'
}

食い違いを生みやすいもの

サーバーとブラウザで結果が変わるもの
書いたものサーバーブラウザ
new Date() の表示ビルド/リクエスト時刻表示した時刻
Math.random()その時の値別の値
window.innerWidthそもそも無い実際の幅
localStorage の設定そもそも無い保存された値
ユーザーのタイムゾーンサーバーのもの利用者のもの
やっていることなぜ食い違うか
Date.now() new Date() を描画で使う呼ぶ瞬間が違う
Math.random()毎回変わる
typeof window で分岐サーバーとブラウザで必ず別の枝を通る
toLocaleString() をロケール指定なしで使う環境で書式が変わる
localStorage を初期値に使うサーバーには存在しない
<p> の中に <div> を置くブラウザが構造を勝手に直す

最後の1つは見落としやすいところです。HTML として不正な入れ子は、ブラウザが黙って修正するので、React が描いた木と合わなくなります。

typeof window は解決策ではない

window is not defined を消すために、こう書きたくなります。

const w = typeof window === 'undefined' ? 0 : window.innerWidth

参照エラーは消えます。ですが、サーバーは 0、ブラウザは実際の値を描くので、今度は hydration の不一致になります。エラーが別の場所へ移っただけです。

React 自身も、エラー文の原因候補の筆頭にこの書き方を挙げています。

サーバーから値を渡すという手

「ページを作った時刻」を見せたいだけなら、Server Component で決めて渡せば一致します。

// Server Component
const now = new Date().toISOString()
return <Clock now={now} />

サーバーもブラウザも同じ文字列を描くので、食い違いません。「ブラウザで計算する必要が本当にあるか」を先に考えると、多くの場合これで済みます。

本番と開発で見え方が違う

何が出るか
開発モード全文のエラー、原因の候補、差分、該当行
本番ビルドMinified React error #418 だけ

本番で #418 を見たら、同じ条件を開発モードで再現させてから原因を探してください。本番のログを睨んでいても、行番号は出てきません。

自分のコードに心当たりが無いとき

ブラウザ拡張が HTML を書き換えていることがあります。シークレットウィンドウで再現しないなら、それが原因です。この場合はコード側では直せません。

出典

このページの内容は上記の一次情報を基準に書いています。個人ブログや二次情報を根拠にしていません。

理解できたか試す

では、このコードはどうなりますか。

use client が付いています。ブラウザで開くと何が起きますか。

この問題を解くこのテーマの問題 3 問