Next15.3で追加された onNavigate でページ遷移をブロックする時のメリット・デメリット
開発環境
- next: 16.0.3
前提
AppRouterを使用しています。
Next.js の Pages Router では、router.events を利用してページ遷移開始 (routeChangeStart) をフックし、その場でキャンセルすることが可能でした。
しかし App Router へ移行後、この仕組みは廃止され、同様のページ遷移ブロックはできなくなりました。
Next.js 15.3 では、新たに onNavigate が導入され、App Router でもページ遷移をブロックできる仕組みが追加されたので、本記事ではその使用感を整理します。
本題
Pages Router 時代のページ遷移キャンセル
あくまでイメージですが、Pages Router の router.events は次のように書くことで遷移キャンセルができていました。
router.events.on('routeChangeStart', (url) => {
if (!confirm('ページを移動しますか?')) {
router.events.emit('routeChangeError');
throw 'Route change aborted.';
}
});App Router に移行するとこれが廃止となり、遷移そのものを止める方法がなくなっていました。
Next.js 15.3 の onNavigate の登場
App Router の <Link> コンポーネントに onNavigate が新しく追加されました。
onNavigate はクライアント側のナビゲーション中にのみ呼ばれるコールバックです。
基本的な使い方
'use client'
import Link from 'next/link'
export default function Page() {
return (
<div>
<Link
href="/dashboard"
onNavigate={(e) => {
if (!window.confirm('未保存の変更があります。移動しますか?')) {
e.preventDefault() // 遷移キャンセル
}
}}
>
ダッシュボードへ
</Link>
</div>
)
}confirm にしか頼れない理由(仕様上の注意)
onNavigate は 同期的に preventDefault() を発火する必要があります。
そのため実質的に ブラウザ組み込みの confirm() のようなAPI しか使えないです。
カスタムモーダルを組み込むことが現状できないので、カスタマイズ性が低いです。
この点は大きな制約となりえるでしょう。
導入時に検討すべきこと
実際にプロダクトに導入して感じたことを列挙してみます。
① ヒューマンエラーによる「離脱の抜け道」が生じやすい
👉 onNavigate が効くのは 対象となる <Link> に対してハンドラを仕込んだ場合のみです。誤って通常の <Link> を使うと、そこから自由に遷移できてしまいます。フォーム画面などでは意図せず離脱を許してしまう危険があります。
② router.push() はガードできない
👉 シンプルに、止めることができないです。onNavigate が機能するのは <Link> に対してのみであり、App Router 全体の遷移ガードとしては不十分です。
③ フォーム画面で離脱防止用途に使うなら実装コストが高い
👉 画面から見えるリンクすべてに導入する必要があり、抜け漏れの可能性が常にあります。
さいごに
Next.js 15.3 の onNavigate によって App Router でもページ遷移をブロックできるようになりましたが、仕様上の制約により
- confirmしか使えない
- <Link> のみ対象で、router.push がガードできない
- 全 <Link> をカスタマイズしないと離脱防止の完全性を得られない
といったデメリットが残ります。
現状は「軽い遷移確認を行う用途」として割り切って、局所的に利用するのが現実的であり、本格的な離脱防止にはまだ工夫が必要そうです。