【対処法】Next.js開発で発生するハイドレーションエラーの原因と解決策
ReactやNext.jsで開発していると、一度は遭遇するであろうハイドレーションエラー。
見た目には問題ないのに、コンソールにエラーが出て不安になる…そんな経験はありませんか?
ハイドレーションエラーは、サーバーサイドレンダリング(SSR)とクライアントサイドの描画結果に差異がある場合に発生します。
本記事では、そのメカニズムと原因、そして再発させないための具体的な対策を、初心者にもわかりやすく整理しました。
ハイドレーションエラーの発生原因
ハイドレーションとは、SSRで描画されたHTMLに対してクライアント側でReactがイベントバインドなどを行うプロセスのこと。
この際、サーバーで生成されたHTMLとクライアントでのJSによる描画結果が一致しない場合に、次のようなエラーが出力されます。
arduinoWarning: Text content did not match. Server: "Hello" Client: "Hi"
原因となる具体例は以下の通りです:
| 原因 | 説明 | 例 |
|---|---|---|
| クライアント限定APIの使用 | windowやnavigatorなど、SSR時に存在しないオブジェクトを利用 | useEffect外でwindow.innerWidthを参照 |
| ランダム値の使用 | SSRとCSRで異なる値になる | Math.random()やDate.now()の直接使用 |
| 状態の非同期取得 | クライアントでのみデータを取得してUIを更新する | 初期表示がloading、CSR後にデータになる差異 |
| 時刻やロケールに依存 | タイムゾーンやIntlにより描画が変化 | 日付のローカライズ表示など |
ハイドレーションエラーを防ぐための対策
ハイドレーションエラーを避けるためには、サーバーとクライアントで同じHTMLを出力することが鉄則です。
以下に具体的な対策を紹介します。
1. クライアント限定処理はuseEffect内に記述
ReactのuseEffectはクライアント実行時のみ動作します。
windowなどのブラウザAPIを使用する場合は、必ずこの中で処理しましょう。
tsxconst [width, setWidth] = useState(0);
useEffect(() => {
setWidth(window.innerWidth);
}, []);
2. ランダム・時刻系の値はSSR側でも揃える
例えばDate.now()を直接使わず、getServerSidePropsやgetStaticPropsで生成し、Propsとして渡すようにします。
tsxexport const getServerSideProps = () => {
return {
props: {
timestamp: Date.now(),
},
};
};
3. dynamic()を使ってクライアントのみレンダリング
どうしてもSSRで描画したくないコンポーネントは、next/dynamicでクライアント限定に。
tsximport dynamic from 'next/dynamic';
const ClientOnlyComponent = dynamic(() => import('./ClientOnlyComponent'), {
ssr: false,
});
4. 条件付きレンダリングで安全確認
typeof window !== "undefined"のようなチェックを使い、SSR時の実行を防ぐ工夫も効果的です。
tsx{typeof window !== "undefined" && <ClientOnlyComponent />}
まとめ:Next.jsではSSRとCSRの一貫性が鍵
ハイドレーションエラーは、見た目に影響が出ないことも多いため軽視されがちですが、SEOやパフォーマンス、アクセシビリティに悪影響を与える可能性があります。
ハイドレーションの原理と原因を正しく理解し、以下のポイントを抑えて開発を進めましょう。
- SSRとCSRで出力を一致させる
- クライアント専用処理は
useEffectへ - 不安定な値(日時、乱数、ブラウザAPI)はSSR中に扱わない
dynamic()でSSRを明示的に制御
これらを意識するだけで、Next.jsでの開発がより安定し、安心してスケールするアプリケーションを構築できます。
articleNestJS 2025 年の全体像:Express・Fastify・Serverless の使い分け早わかり
articleNestJS 監視運用:SLI/SLO とダッシュボード設計(Prometheus/Grafana/Loki)
articleNestJS クリーンアーキテクチャ:UseCase/Domain/Adapter を疎結合に保つ設計術
articleNestJS Decorator 速見表:Controller/Param/Custom Decorator の定型パターン
articleNestJS 最短セットアップ:Fastify + TypeScript + ESLint + Prettier を 10 分で
articleNestJS × ExpressAdapter vs FastifyAdapter:レイテンシ/スループットを実測比較
articleSvelte のコンパイル出力を読み解く:仮想 DOM なしで速い理由
articleTauri で Markdown エディタを作る:ライブプレビュー・拡張プラグイン対応
articleStorybook で“仕様が生きる”開発:ドキュメント駆動 UI の実践ロードマップ
articleshadcn/ui で B2B SaaS ダッシュボードを組む:権限別 UI と監査ログの見せ方
articleSolidJS の Control Flow コンポーネント大全:Show/For/Switch/ErrorBoundary を使い分け
articleRemix で管理画面テンプレ:表・フィルタ・CSV エクスポートの鉄板構成
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来