1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Next.jsでHydration Errorに遭遇したので原因と対策をまとめる

1
Last updated at Posted at 2026-06-09

はじめに

はじめまして、Web開発を初めて半年のしょーへいです。
現在、 React や Next.js 、Prismaを使用して「筋トレ記録App」を作成しています。
今回はApp作成中に発生した"Hydration Error"について備忘録の意味を込めて記事にしてみようと思います。
わからないことが多いので、アドバイスなどいただけると嬉しいです。

事象

筋トレ記録Appで、「カレンダーで選択した日と同じ日に行ったトレーニング内容を表示する機能」を作成していました。
以下の様なソースコードを書いたところ、"Hydration Error"が発生しました。

import Calendar from "react-calendar";

export default function CalendarFunction (){
    const [date, setDate] = useState(new Date());  //初期値で実行時の時間をセット
    ・・・
    return(
        <>
        <Calendar
            locale="ja-JP"
            onChange={(value) => setDate(value as Date)}
            value={date}
          />
        <p className="pt-4 text-center">
          {date.toLocaleDateString("ja-JP", {
            year: "numeric",
            month: "numeric",
            day: "numeric",
          })}{" "}
          の記録
        </p>
        </>
    );
}
[browser] Uncaught Error: Hydration failed because the server rendered text didn't match the client. 

原因候補

今回の"Hydration Error"は、
new Date() による初期表示の差分と、
react-calendar のSSR時の描画差分が原因候補でした。

"Hydration"とは、
サーバーが作ったHTMLに、ブラウザでReactの機能を結びつける処理のことです。
つまり、"Hydration Error"とは、
サーバーで作ったHTMLとブラウザ側で描画したHTMLの内容が一致していないことです。

補足:
ブラウザとは、
ChromeやSafariといったユーザーが操作するユーザーインターフェイスのこと。

サーバーとは、
インターネット上で稼働しているコンピュータで、ブラウザなどから送られてきたリクエストに応じて動作します。

内容調査

怪しい箇所Ⅰ : useState

"2026-06-01-23:59:59"にアクセスした場合のuseStateの挙動を例に説明します。

①ブラウザがリクエスト送信 (ブラウザ側からサイトに"2026-06-01-23:59:59"に初回アクセス)
②サーバーでSSR (サーバーからHTMLを作成)
③HTML受信・表示 (作成したHTMLをブラウザへ反映)
④Hydration (ブラウザ側でReactがイベント処理などの紐付け)

①〜④の流れで処理が行われます。
②ではサーバー側のdateに2026-06-01-23:59:59 が代入されます。
しかし、④のHydration時にはブラウザ側でもコンポーネントが実行されるため、
useState(new Date()) が再度評価されます。

その際に日付が変わっていると、
サーバーで生成したHTMLとブラウザで生成したHTMLの内容が一致せず、
"Hydration Error"が発生します。

補足:
SSRとは、HTMLをサーバーで生成してからブラウザに送る仕組みのこと。

怪しい箇所Ⅱ : react-calendar

react-calendarは内部で現在日時やlocaleに依存した描画を行うため、
SSR環境ではサーバーとブラウザで生成されるHTMLが一致しない場合があります。

別件問題箇所 : カレンダーとDBに保存されている日付の比較

createdAtはUTC基準で扱われます。
そのため、日本時間の2026-06-02 00:30は、
UTCでは2026-06-01 15:30として扱われます。

以下のようなソースコードで比較を行うと、選択した日付で適切なトレーニング記録内容が表示されない問題が発生します。

training.createdAt.getDate() === date.getDate()

対策

対策Ⅰ : useState

dateの初期値をnullにし、
ブラウザでのみ実行されるuseEffect内で現在日時を設定することで、
サーバーとブラウザの初期描画内容の不一致を防ぎます。
なお、useEffectはHydration完了後にブラウザでのみ実行されるため、
SSR時のHTML生成には影響しません。

const [date, setDate] = useState<Date | null>(null);  //初期値でnullをセット

useEffect(() => {
 const today = new Date();
 setDate(new Date(today.getFullYear(), today.getMonth(), today.getDate()));
 },[]);

if (!date) return null; //dateが設定されるまで描画しない

対策Ⅱ : react-calendar

react-calendarをSSR対象から外すことで、サーバー側で描画を行わせないようにします。
サーバー側で描画を行わせず、ブラウザ側だけで描画を行うことで"Hydration Error"が発生しないようにしました。
今回の事象についても影響している可能性があったため、
SSR対象から外して検証を行いました。

import dynamic from "next/dynamic";
import "react-calendar/dist/Calendar.css";

const Calendar = dynamic(() => import("react-calendar"), {
 ssr: false,
 });

別件問題対策 : カレンダーとDBに保存されている日付の比較

createdAtはUTC基準で扱われるため、
日本時間の2026-06-02 00:30は、
UTCでは2026-06-01 15:30として扱われます。

そのため、日本時間の開始時刻と終了時刻をDateで作成し、DB上で範囲検索する方法で対処しました。

const start = new Date("2026-06-01T15:00:00.000Z"); // JST: 2026-06-02 00:00
const end = new Date("2026-06-02T15:00:00.000Z");   // JST: 2026-06-03 00:00

const trainings = await prisma.training.findMany({
  where: {
    createdAt: {
       gte: start,
       lt: end,
    },
  },
});

まとめ

今回のHydration Errorは、new Date() によってサーバー側とブラウザ側で初期表示がずれる可能性があったこと、また react-calendar が内部で日付やlocaleに依存した描画を行うことが原因候補でした。

対策として、初期表示では日付を直接表示せず、useEffect でブラウザ側だけで日付をセットするようにしました。また、react-calendardynamic import を使ってSSR対象から外しました。

さらに、DBに保存される createdAt はUTCで扱われるため、カレンダーの日付と単純に getDate() で比較すると、日本時間とのズレが発生する可能性があります。そのため、選択日の開始時刻と終了時刻をUTC基準で作成し、Prismaの gte / lt を使って範囲検索するようにしました。

学んだこと

今回の調査を通して、
Hydration Errorは単純な実装ミスだけでなく、
日時やlocaleのような環境依存の値でも発生することを学びました。

今後はSSRとCSRの違いを意識しながら実装していきたいと思います。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?