LIFULL Advent Calendar 2025 20日目の記事です。
はじめに(結論)
結論から書くと、Next.js(App Router)で「ページリロード時だけ」を正確に検知する方法は見つけられませんでした。
最終的に、この要件は実装スコープから外す判断をしています。
本記事では、beforeunload や PerformanceNavigationTiming といった一見使えそうな Web API を実際に検証し、なぜそれらが App Router では成立しなかったのかを整理します。
やりたかったこと
Next.js(App Router)を使用したアプリケーションで、「ページリロード時」だけを対象に、ある処理を除外する必要がありました。
ここで重要だったのは次の点です。
- 対象は ページリロードのみ
- 以下は 対象外
- 画面遷移(
<Link>/router.push) - ブラウザの戻る / 進む
- 画面遷移(
着手する前は
「リロード検知なら、何か Web API があるだろう」
と考えていました。
しかし、App Router を前提にした瞬間、
「そもそも前提としている挙動が噛み合っていない」
という問題にぶつかりました。
App Router における「遷移」と「リロード」
まず整理しておくべきなのが、App Router における画面遷移が ブラウザのページ遷移ではない という点です。
Next.js(App Router)で <Link> を使って画面遷移すると、内部では次のような処理が行われます。
-
<Link>をクリック - RSC Payload(シリアライズされた React Tree)を取得
- React Tree を差し替えて画面を更新
このとき、
- HTML ドキュメントは再読み込みされない
-
window/documentは破棄されない - JavaScript の実行環境はそのまま維持される
という状態になります。
見た目は「ページが変わった」ように見えますが、ブラウザ的にはページを移動していません。
beforeunload を試す
最初に試したのが beforeunload です。
「ページから離れる直前」を検知できる API なので、直感的にはリロード検知に使えそうでした。
期待していたこと
- リロード時に発火する
- App Router の画面遷移では発火しない
- よって「リロードだけ検知できるはず」
実際に起きたこと
- App Router の画面遷移では発火しない
→ これは想定通り - しかし リロード「だけ」を判定することはできなかった
なぜ beforeunload ではダメだったのか
beforeunload が通知するのは、
ページが破棄される直前であること
だけです。
そのため、
- リロード
- タブクローズ
- 別ページへの遷移
- 戻る / 進む
といった操作を 区別することができません。
「ページから離れる」という事実は分かっても、なぜ離れたのかは分からない という仕様です。
この時点で、
- 「リロードだけを知りたい」
- 「理由は問わない API」
というズレがあることが分かりました。
PerformanceNavigationTiming を試す
次に試したのが PerformanceNavigationTiming です。
MDN を確認すると、ナビゲーションの種類を取得できそうでした。
reloadnavigateback_forward
これを使えば、リロードだけを判別できるのではないかと考えました。
App Router での結果
しかし、App Router 環境で確認してみると、
-
<Link>による画面遷移を行っても - ナビゲーション種別は常に
reloadのまま
という結果になりました。
なぜこうなるのか
PerformanceNavigationTiming が対象としているのは、
ブラウザが行うページ遷移
です。
App Router の画面遷移では、
- 新しい HTML ドキュメントは読み込まれない
- ブラウザとしての「ページ遷移」は発生しない
そのため、画面が切り替わっても、ナビゲーションとしては扱われません。
結果として、
- 最後にページが読み込まれたときの状態(
reload) - それ以降は変化なし
という情報しか取得できませんでした。
なぜこの 2 つでは要件を満たせなかったのか
ここまでを整理すると、次のようになります。
| 試したもの | 分かること | 分からないこと |
|---|---|---|
beforeunload |
ページが破棄される | 離脱の理由 |
PerformanceNavigationTiming |
ブラウザのページ遷移種別 | SPA 内の画面切り替え |
| App Router | 画面表示の切り替え | ブラウザ遷移の発生 |
それぞれ 前提としている挙動の範囲が異なる ため、
組み合わせても「リロードだけを検知する」という要件は満たせませんでした。
まとめ
-
beforeunloadでは、リロードかどうかを判別できない -
PerformanceNavigationTimingは、SPA の画面遷移を対象としていない - App Router の画面遷移は、ブラウザのページ遷移ではない
学び・教訓
- 同じ「遷移」に見えても、ブラウザが扱う遷移と Next.js が扱う遷移は別物
- ブラウザ API は SPA の内部状態を前提にしていない
- 「何を検知したいのか」と「API が何を扱っているのか」を
最初に揃えて考えないと、簡単にハマる
同じように「リロードだけ検知したい」と考えている方の
遠回りを一つでも減らせれば幸いです。